Blog / Build in public ·
Status update: working on webhooks you can trust
Developers asked me what Payblast does when store events arrive twice, late, or out of order. This is what shipped, what is in review, and what comes next.
I started Payblast as a prototype for my own apps. It is a RevenueCat alternative, and it is still early. Lately, a few developers on X have asked me good questions about it. What happens when the same purchase event arrives twice? What if a refund arrives before a delayed purchase? And how do late, out-of-order, or missed webhooks get reconciled?
Before replying, I tried those cases against Payblast, and the answers were not good enough. A notification delivered twice sent the developer webhook twice. A renewal that arrived late could turn access back on after a refund.
I also read public complaints about RevenueCat, Adapty, and other subscription tools, on X, Reddit, GitHub, and community forums. Three things came up again and again: webhooks that arrive late or not at all, events that arrive out of order, and sandbox users counted in real metrics. This is not one vendor's problem. Stores deliver notifications at least once and in no guaranteed order, and every backend has to handle that.
What shipped
The first change is merged. Payblast now applies each event from the App Store, Google Play, and Stripe once, in the order the store signed it, not the order it arrived in.
- A duplicate delivery is recorded and has no effect. Access does not change and no second webhook goes out.
- A refund cannot be undone by an older event that arrives late. That event is recorded as stale.
- Notification types Payblast does not recognize are recorded as ignored. They never grant access.
-
Each customer has an event history at
GET /v1/customers/:app_user_id/events, read with your secret key. Every row shows the outcome and the entitlements after it. -
Customer reads accept
?environment=sandbox, so you can check sandbox and Stripe test-mode purchases. They stay out of production reads.
To check all of this without waiting on a store, I added a replay tool, mix payblast.replay. It plays one story for
each store: the same purchase delivered twice, then a refund that arrives before a delayed
renewal. This is a simulated replay. Each delivery is signed and goes through the same
handlers as the real webhook endpoints, but it all runs locally and no store sandbox is
involved.
Coming next: webhooks to your server In review
The other half is the webhook Payblast sends to your server. That work is in a pull request that is still in review, so none of this is live yet. When it merges:
- Payblast answers the store right away and sends your webhook from a durable queue, so a slow endpoint never holds up the store.
- A failed delivery is retried, waiting from 1 minute up to 24 hours between attempts. Every attempt is logged.
- Each webhook carries the complete event in a versioned payload: event id, the time the store signed it, a per-customer sequence number, the user's aliases, the entitlements after the event, and the price and period.
- A delivery log in the dashboard shows each delivery's status, attempts, and last error, with Resend on every row and a Send test event button.
-
If your server misses something,
GET /v1/customers/:app_user_id/events?after_sequence=Nreturns the events after the last sequence number you stored.
The same pull request changes how new customers are counted. Right now, sandbox users are included, the same problem I kept reading about. After the change, a customer counts from the day a store first reports them in production. Sandbox testers, TestFlight, and Stripe test mode only show up when Sandbox data is on.
What's next
- Safer account transfers and restore across iOS and the web. When someone buys on an iPhone and later signs in on the web, or restores on a new phone, the purchase should move to the right account once, and your server should hear about it.
- Real Apple notification handling. Payblast will accept App Store Server Notifications in the signed form Apple sends and verify them against Apple's certificates. Then the replay can run against Apple's own sandbox instead of a simulation.
Most of this update started as someone else's question, so please keep them coming. If webhooks, refunds, or sandbox data have caused you trouble in production, I would like to hear what happened. Reply wherever you found this post, or email hello@bitscorp.co.
Try it with your own app
Create a project, attach your store products, and check one entitlement. One or two apps are free to start, with no card required.