Reversing Safeway's private APIs to automate coupon collection
blog.jonlu.ca
blog.jonlu.ca
Aside: At the end of the article they have a "Bonus: Speeding things up" section where they automate adding 300~ coupons via 300 HTTP connections in 5 seconds (instead of 60~ seconds).
In my opinion if you're going to automate stuff like this, you should do so with the goal of minimizing disruption (and frankly, detection). They run the script automatically at midnight in the background via cron, so why was going slower problematic? 300 requests in a span of 5 seconds seems much more likely to trigger an IDS[0], get flagged as unusual in the logs, or similar than 300 over 60 seconds. Particularly at midnight.
I'd be trying to look at human as possible and not set off automated security systems. Heck you could add a randomized delay (e.g. 1~2 seconds) between requests and it would still be completed inside of 10 minutes. Plus then nobody can reasonably accuse you of trying to "DoS" them/violate the CFAA.
[0] https://en.wikipedia.org/wiki/Intrusion_detection_system
If I was more worried about Safeway catching on I'd probably do something as you suggested (at the very least I'd add user agent headers and the other cookies expected from a real session, as right now it's trivial to detect my requests).
Sneaker bots do this exceedingly well - it's a constant cat and mouse game to make the requests look as human as possible, very interesting space right now.
These projects are fun IMO, but it's best to not hammer anyone's servers if it isn't necessary.
It doesn’t blend in as well...
I'm honestly surprised they weren't rate limited. I mean, your SPA would have to be really messed up to make more than a handful of requests a second (even then why aren't you using sockets?) - So it's super reasonable to say that if anyone is making over 100req/s then maybe they should take an hour timeout.
If you don't want to get caught doing this then you'll want a randomized delay like you said, and preferably a pool of IP addresses you proxy through. This is to prevent the automated stuff from catching you though - a human can still look at your account and wonder why you have every coupon activated 24/7.
Q: If you're worried about detection, why would one blog about it/post it on HN?
Isn't the fastest way to shutdown a loophole to make it public?
$(".grid-coupon-clip-button button").click();
Though of course if Safeway's website does have a global `$` from jquery then that would take precedence.
> what does this do exactly?
The command allows you to click all buttons in the page at once:
• The dollar sign is an alias for jQuery [1];
• The text between double quotes is a CSS selector;
• Select every DOM element with a “grid-coupon-clip-button” and “button” CSS class;
• The thing at the end is a JavaScript function call which triggers an “onclick” event [2];
- Select every button with a parent element that has the css class “grid-coupon-clip-button”
The mental effort of going to the site at all was what I was trying to circumvent - now I don't even need to think about it (the clearer abstraction is that going from "2 to 1" is a much less drastic jump than going from "1 to 0" - completely removing the overhead is what makes this worthwhile, IMO, not the actual speed of the actions)
https://gist.github.com/pbojinov/d572b5494a4f26390aeb5136d70...
Rube Goldberg Machine's are not good in software projects...
https://www.safeway.com/justforu/coupons-deals.html
for(let i=0;30>i;++i)setTimeout(function(){btn=document.querySelector("#coupon-grid_0 > div.coupon-grid-container > div.load-more-container > button");btn && btn.click()},1e3i);elems=document.querySelectorAll(".grid-coupon-clip-button button");for(let i=0;i<elems.length;++i)setTimeout(function(){elems[a].click()},500i);
Anyone remember Coupon Guy from 4chan in the 2009/2010 timeframe? You could make your own "Buy 1 Get 1 Free" or variant (for both N, "buy 1 get 10", or for kind of coupon, like "buy 1 get half off") Tv, Xbox 360, near anything etc. For Walmart, Best Buy, other chains etc. The tools were passed around stegonographically in the instruction images. Since coupons weren't properly accounted for until they hit a place in Texas, whole threads full of Anons over the course of weeks fabricating working coupons.
Until they stopped working, and of course rumors of "the FBI" apparently grabbing the guy.
I never did figure out what happened tech wise under the hood there.
Whoa, really? I remember the coupons flying around 4chan of course. Had no idea the tools were in the images.
There might have also been some steganographic images, but I know for sure I saw several of the image+archive kind around then.
They did get him: http://www.thesmokinggun.com/documents/internet/fbi-busts-4c...
This story about a similar arrest has a good explanation about how to fake coupons: https://www.wired.com/2015/05/inside-a-million-dollar-dark-w...
In UK, we have items which when reduced all they do is add the new price to the end of the bar code.
ie. If some product is barcode of 3035555074225 and it's then reduced in price, the reduction including some checksum is added.
So, if the new price of 3035555074225 is 50p, the code becomes 303555507422500502 (Where we add 0050 for the price, and 2 is the checksum).
Next time you are in the supermarket just look at the barcode and you'll spot the pattern.
So for your example, maybe there was some EPOS system that the guy had inside understanding of how barcodes worked on coupons and could easily pair them.
I did something similar (read-only) for Home Depot Truck Rentals. To check if the truck was available at my local store, each time, you had to put in your zip code, and click a couple of times. Once I found that was an API, I rebuilt the call in Postman and kept hitting that endpoint until a truck was available.
That way I could check really fast.
The twist: None of it mattered because their data itself didn't update accurately (I saw one in the parking lot and they had one available and never updated their site). :)
My favorite example is the Domino's "Your pizza is ready" signal. Since the data feeding the signal also feeds the store's performance analysis (i.e. they track how fast employees are getting pizzas ready), there's significant incentive for employees to lie to the algorithm and hit "It's ready" before it's physically ready, on the assumption that customers will take nonzero time to wander over and show up for pickup.
It's the same with fast food in general. One of my first jobs was at a McDonald's where the standard practice was to hit the 'finished' button for any order that was taking 'too long', at which point the people doing food prep would have to keep track of a sequence of three or four orders in their head. As you can imagine, errors were constant, but customer satisfaction was less important to management than satisfying this imaginary metric.
To add a bit of color to what's happening:
- You don't actually hit an "It's ready" button. When you knock the pizza off of the makeline screen, it transitions from "so and so is making your food" to "it's cooking".
- The "It's cooking" phase is just a simple timer. It's supposed to be adjusted to the time of the conveyor oven (which can vary from ~3-9 minutes depending on the particular oven they use). Most stores never customize that setting in the system, and it stays at the default. And other stores may have ovens running at two different speeds.
- Average make time is one of the metrics monitored by corporate audits, so you do have gaming of the system by knocking pizzas off the make screen early. But you can also have a backup at the oven during rushes, where food sit at the end of the makeline ready to go in the oven when capacity opens. At the same time, some items such as wings have to go through the oven twice (depending on the oven configuration). In both of these cases, even without gaming the system there will be dissonance between actual cook time and the cook time shown in the pizza tracker.
- For those that did try to game the performance metrics, it came with an equal headache internally beyond just that "It's cooking" timer. The scheduling system used that average make time as an input into calculating labor needs. The more you knocked stuff off the makeline early, the more optimistic the scheduler became and the more you'd have to override the suggested schedule and the more "Labor Waste" you'd create by having more people working than the system thought you needed.
It was really quite fascinating to see that system transition before I started my career. I witnessed it at two stores under two different franchises - one did the bare minimum to comply and the other one embraced the new system and it's capabilities. There were a lot of incredibly capabilities and forecasting optimizations that were made possible by the update. But they all presumed accurate data in the system at all times. But in many cases, managers were either disincentivized to do what was required to maintain that level of accuracy or transparency, or a component of the system would be designed in such an idealistic fashion that it didn't allow for the amount of pragmatic flexibility it needed. Both of which have been really valuable lessons I've taken forward with me.
I did the same thing to find available camping spots in Hawaii because they are super difficult to get. Wrote a script that would query their "API" every 5 minutes and alert me if a spot became available anywhere.
Presumably those campsites are permitted by some government agency (NPS, BLM, the state of Hawaii, etc.), and presumably that agency designed permitting system with the assumption that people with limited time and attention would be vying for the permits by having to visit the site themselves to get one.
This encodes a particular definition of fairness: that those who register early, or are very motivated, or simply those with a lot of free time to refresh the site, will get permits.
I can also whip up a quick script to replace refreshing an unprotected HTTP API with a notification email. Does that make me more deserving of the camping spot?
Why is this definition changing with knowledge something that should be considered wrong? The ability to do this work isn't gated to certain people except by knowledge, and the knowledge itself isn't gated. For the longest time this has been considered a fair way of doing things to get an item in limited supply.
That's quite a very large assumption that I don't think we can accept as fact.
> that those who register early, or are very motivated, or simply those with a lot of free time to refresh the site, will get permits.
Perhaps someone who writes a script would count as "very motivated"?
> Does that make me more deserving of the camping spot?
"Deserving" has nothing to do with this in the first place. The only way that works is if you define "those who register early", "motivated", "a lot of free time" as "deserving". I could maybe see an argument for the first two classes of people as being "deserving", but I don't think you can justify "I have a lot of free time" as a reason for deserving anything, really.
Doing first-come, first-served based on availability and the random possibility of a cancellation isn't ever going to be a "fair" system. This sort of system is put into place because it requires very little coordination and work on the part of the agency that maintains the reservations. Holding that up as some sort of standard for fairness, and suggesting that anyone who thinks outside the process is wrong... is a little much.
I think you're right the agency probably didn't sit down, write down definition of fairness, then design a permitting system around it. They probably implemented the cheapest/easiest digital analog they could find to a traditional fax-in/walk-up first-come/first-served permitting system.
However, the intentionality of the implementers was not central to my argument.
The system was created by (probably) non-technical people under a certain set of assumptions: namely, that this digital first-come first-served system would function approximately like the old paper one, but with fewer dead trees and toil. The old one was rate-limited by having to call an office and probably talk to a human, and the assumption that if you call every 5 minutes that human will probably get annoyed with you and stop answering your calls. The new one is rate-limited by the assumption that most campers simply can't spend all day refreshing a website.
The traditional first-come first-served system isn't intrinsically "ethical" or "fair" for some classes of people (as you've astutely pointed out), at best it's a crude approximation of some version of fairness. While crude, it was established by a democratically-elected government tasked with allocating a shared resource. "People who can automate HTTP API calls" and the nearby "people who can hire people to automate HTTP API calls" (as has actually happened with some outdoor permits) were almost certainly not in the groups of people the government was seeking to advantage by choosing this system, and I think most engineers are smart enough to be able to intuit that.
So the root of my comment was this: GP is using special knowledge they have (and probably worked hard for) to extract more of a public good than the public really intended to have access to.
* Is that fair to everyone who doesn't have GP's knowledge? Do people like the GP deserve more camping spots than others? This is a public resource, not sneakers, so fairness is important. * If everyone with programming knowledge acted the way GP acts, would that maximize the public good? * If everyone with programming knowledge acted the way GP acts, would the system even function at all?
My answers are basically: * No. Everyone who wants deserves an equal chance at the spots. If there's more demand than spots, it's the government's job to decide. Random programmers on the internet intentionally subverting the government's intentions is wrong regardless fairness (or lack thereof) of the original system). It would nearly-minimize the public good. Only programmers and people who can hire them would get popular camping spots. This is a real problem is popular outdoors areas around tech hubs. There's a reason NPS will only accept old-fashioned faxes provably not sent from a free online relay for the most popular Sierras routes in CA (e.g. the JMT and much of Yosemite), and it's not because they want to rock like it's the 80's or because the government is backwards. It's because assholes tried to spam the process with automation. I think some (like Half Dome) were migrated to a new lottery system on outdoor.gov this year. * The system would completely collapse, and most smart programmers could predict that. The government would have to spend more money on servers just to serve bots pinging the registration system constantly, or it would crash. Even if they did that, the people who gots slots would be a vanishingly small subset of the population (programmers) or people who can hire them. Likely, a grey market for "scalped" permits would arise.
Specifically, consider Snapnames -- a company born out of the notion that snapping up a domain name coming up for renewal was something that was legitimately fairly awarded to the automated process that was fastest.
Without a script it relies on your free time and refreshing the script every 15 minutes. What if you have a full time job far away from a computer, are you less deserving than anyone with a lot of free time to refresh the page?
I just poked at the main JS file for a couple of minutes until I found the statement I needed:
mainController.loadNextScreen()
This turned what would have been a 3+-hour slog into 5 minutes, and I passed the quiz just fine.Yes, Jenny’s number.
Though as a rule I don't bother with most of these programs, mainly due to the inconvenience factor.
I've lost hope of maintaining my privacy through any actions I can take individually. A legislative solution is required.
The club card works across multiple credit cards and cash. That’s the main difference. And you might possibly track 2 people who control the household (wife gets one, husband gets one)
I don’t have an Android device to test these instructions on but they seem plausible and corroborated: https://supportcentre.natwestinternational.com/Searchable/91...
When I made a return for an Apple Pay purchase at Target, they saw that the transaction was marked “Tapped” as card type and took my word for which card was correct. Who knows how long that’ll last. Where I really expect to run into problems down the line is when I try to take advantage of the insurance benefits offered by cards and can’t produce an invoice/receipt with the expected card number on it.
Yes, it won't be perfect (especially if one uses multiple cards), but many stores are getting extremely sophisticated with their techniques for profiling customers.
Currently west coast gasoline is ~$4.00/g, so that's about 2.5-5.0% discount, which is in line with GP said.
At the self checkout machines at Giant there is a "Forgot my card" option. It gives you all the discounts without entering anything.
Too bad? I don’t have a car
I thought that number was drilled into everyone's brain, but I guess you have to be a certain age.
According to my last receipt, my YTD "savings" has been $959.68
So I am not the only Jenny out there!
Similarly for all these other rewards programs you see at restaurants nowadays. I will never understand the idea of using a phone number as authentication without any additional PIN or text message or anything. If I have an acquaintance that I know goes to a lot of movies, and I either know or can find their phone number, I can drain their rewards account. Or you can drain their Safeway rewards account, etc. I wonder how much longer the situation will last?
It seems like coupled with couponing, you can build a decent price tracker that can tell you if you're actually getting a good deal. (like https://camelcamelcamel.com/ for amazon, https://steamdb.info/sales/ for steam games)
Otherwise I've noticed that many(though not all) coupons are for items which recently had their base price increased to make the coupon seem like a better deal than you're actually saving.
The fact that Aaron's Law never went through has disturbed me...
Sure, the judge who heard this case said this would be an "overly broad" interpretation of the law at the time, but the question has come up in subsequent criminal cases as well. I'd feel better if that was actually codified and not left up for interpretation by other judges or courts.
0 - https://en.wikipedia.org/wiki/United_States_v._Drew#Indictme...
There was also a review of the database directive last year that summarize all the precedence on this: http://data.consilium.europa.eu/doc/document/ST-8466-2018-IN...
There's an ongoing case Linkedin v HiQ where Linkedin said HiQ was scraping publicly available linkedin profiles but there was a robots.txt that told them not to. HiQ kept doing it until Linkedin threatened them under the CFAA. HiQ just won a preliminary injunction to get to continue where the court said it was unlikely that they were violating the CFAA but they might change their minds as the case progresses:
https://www.eff.org/deeplinks/2019/09/victory-ruling-hiq-v-l...
I'd argue the above case does not apply here.
HiQ v Linkedin kind of hinges on the implicit authorization given by making an API public. Here Safeway gave explicit authorization so I suppose it might come down to the TOS. Then again all Safeway has to do is revoke your account and you're gone, so I don't really know why anyone would be to worried about them coming after you with the CFAA. Cant be worth the effort.
On a side note, Safeway and Sobeys in Canada don't have a loyalty program, instead they piggy back off of Air Miles. All of the special offers available just amount to bonus Air Miles, so they're not actually that worthwhile (IMO).
I don't think Air Miles are completely useless. I do remember redeeming air miles at least once in the past for a one-way trip somewhere...
For example, I needed to rent two cars on a trip last year. The first car I was able to rent through Air Miles, but I had to pay the taxes and any fees beyond the base price myself so it didn't feel like much of a deal. The second car required more Air Miles than I had, so I had to pay for the whole thing myself, I couldn't do part on Air Miles and part cash.
I ended up renting the second car through Costco and felt like I got a better deal overall than I did with the first car.
Maybe rental cars aren't the best way to use them.
If it's indeed the case that he has no residency, he's one report to CIRA away from the domain cancelled.
I'm not sure if using this API is any different but a few months ago Safeway made a change that only lets you have 20(?) coupons at any time. After you add more it kicks off your oldest one. Which sounds like plenty but if you're adding every single coupon you're gonna get a ton that you have no desire for ($1 off diapers when you don't have a kid etc).
It worked for a while, though!
You can also use Puppeteer for this purpose (using headless Chrome) https://github.com/GoogleChrome/puppeteer
The problem came at checkout -- whenever I typed in my phone number (to apply the coupons from the loyalty account), the point-of-sale system would hang for tens of seconds while loading and then trying to apply all those coupons.
This gives me a list of things to buy but more importantly I know what's on sale. If I just added all the coupons
I'd still have to scan the list of things to find what I want to buy, but then I'd have to track them elsewhere, because the built in list would be useless.
Safeway actually made a decent app that helps me shop faster. This feels like it would ruin that.
> I’m not someone that “needs” to get the best deal - if what I’m buying has a coupon then great, but I’m not going to change my purchases based on coupons
The author will buy what the author will buy, and he may happen to get a discount if any coupons apply.
1) Saves you money (versus the retail price of the item)
2) Makes you more likely to buy the product on the coupon.
Your use of coupons adheres to both value #1 (saves you money, because you know what's on sale) and value #2 (advertises products on sale, increasing your chances of buying them). This is the core advertising model of coupons and why they've been popular forever.
This automated use of coupons saves you money if you happen to purchase an item for which a coupon exists, without requiring you to do any extra work to save that money — but also without having the desired advertising outcome of making you more likely to purchase that item.
This compares well to adblocking. Internet advertising on websites:
1) Saves you money (versus subscribers-only paywalls)
2) Makes you more likely to buy the product on the advertisement
And in that analogy, ad-blockers are directly equivalent to coupon auto-adders: they allow you to save money without having the desired advertising outcome of making you more likely to purchase that item, in a fully-automated manner that doesn't require you to exert any effort doing so.
Coupons are a precursor form of advertising ("pay-per-clip" :) where you are paid money to view the advertisement, and are more likely to commit to buying the product when you 'clip' the coupon — it's a marketing psych thing. They also have perfect tracking, since retailers provide coupons to manufacturers along with purchase date and location.
Coupon autoclippers break that agreement, such that you're 'paid' for being influenced by the advertisement without ever having been influenced. The coupons are no longer valid for tracking the effectiveness of advertising A/B tests in different markets (your Safeway account's zip code is surely part of that data). They are no longer proof that you viewed an ad at all.
You're not wrong that this app would ruin how you shop quickly, but you're also shopping quickly using a list of products that were predetermined by Safeway and/or other marketing divisions to be of maximal interest to them for you to purchase. As long as you're okay with that, coupon clipping is an excellent approach. For others, autoclippers would minimize the price paid without changing their purchasing methods (which may be paper-based, brand-focused, or random-chaotic)
You have a well-written and accurate comment, but I'm overwhelmingly distracted by the combination of the adorable "pay-per-clip" and the historied debate about how to handle an emoticon smiley at the end of a parenthetical. (conclusion: you're doing it wrong and are a terrible person!....but, "pay-per-clip"...tee-hee)
Interesting way to compare this auto script to adblocking.
They have perfect tracking of completed purchases, but have no idea how effective their ad was for the people who didn't buy it. Did they miss the ad entirely because of poor placement? Did they see the ad but not value the deal? Did they clip the coupon but not make it to the store before expiration? Did they clip the coupon but find a better deal in the store? Did they clip the coupon, find the store was out of stock, and not feel like dealing with the store's rain-check process (if any)?
Arguably the tracking isn't perfect for completed purchases, even. They get purchase date and store location, but that's not all that much: online retailers get much more information when someone clicks on a banner ad and then completes the purchase. They can even get some information out of a failed sale, depending on how far the potential customer made it through the process before bailing.
I also use the Safeway app, which I think is one of the better apps out there, but sometimes they add in a coupon for something free. Unless I'm looking for that, I might miss it. I'd like to modify the script to look for and notify me when it finds them.
Reading this inspired me to finally release it here: https://github.com/davecardwell/publix-coupon-clipper
This is an insane trend in supermarket usability. You want to offer low prices, but only to people who go through a practice round of online shopping before doing it in person?
Why are we doing this to ourselves?
I get on a basic level it's a form of price discrimination, it just seems unfathomable that this bizarre skeuomorph, totally dependent on a series of random historical developments, would actually happen to line up with the way to extract the most return from your customers and/or give them the best experience.
But hey, I found the perpetual fake "50% off" sales at JC Penney's crazy making, and when they reversed that for more honest pricing they almost went out of business. I know I'm just shaking my fist at irrationality for no reason.
It still bothers me though.
So now people will complain and your inbox is flooded with "please fix this" because people feel entitled when all you really wanted to do was just try some cool thing.
https://gist.github.com/danielatdattrixdotcom/bd6a05e3d8c499...
Executed via cron every day at a time before I would make a potential grocery run meant the coupons were already waiting on my account when I went to the store.
Safeway by contrast is part of the Albertson and Albertsons and its Subsidiaries is the 3rd largest Supermarket chain.
btw- how many people here have contributed directly to PostgreSQL in any way.. "food for thought"