GhonaimStudio

Home / Work / W-03

Checkout that stops failing quietly

iPhone Photography School — Online photography school, 800K+ students, Latvia

Built as senior engineer at IPSPayments2025Latvia, remote

The iPhone Photography School home page, where the checkout paths begin
Industry
Online education
Size
Four payment rails; in-app purchases are about 8% of company orders
Region
Latvia, remote
Stack
Braintree, StoreKit 1 and 2, Apple Pay, iOS webview
Timeline
2025
Status
Live

The problem

Customers bought through four different paths: card payments on the web, Apple in-app purchases on two generations of StoreKit, Apple Pay, and an iOS webview. Failures on those paths were not loud. A purchase that silently failed looked like a customer who changed their mind, and only the noisiest cases ever reached support.

What we built

One error model across four rails

Typed exception classification for every payment failure path, so a declined card, an expired receipt and a webview that lost its session stop looking like the same unknown error.

StoreKit 1 and 2 handled honestly

JWS detection to tell the two generations apart, and purchase restore on StoreKit 2 with retry and de-duplication, so restoring does not grant or charge twice.

Apple Pay with a fallback

Apple Pay version 14 with an explicit fallback to version 2, plus resilience work on the iOS webview and PWA paths that customers actually buy through.

Failures that show up on a dashboard

Sentry context attached across the failure paths, so a broken rail appears in monitoring instead of arriving as a support ticket days later.

  • Braintree
  • Apple StoreKit 1 and 2
  • Apple Pay
  • Laravel
  • iOS webview
  • Sentry

Screens

The iPhone Photography School home page, where the checkout paths begin
iphonephotographyschool.com, the platform behind the checkout.
iPhone Photography School home page
iphonephotographyschool.com, where the checkout and subscriptions live
The products page of iPhone Photography School
The products page: every purchase here runs through the payment rails described above

How we worked

Our founder proposed this hardening work rather than being assigned it: the failure classification came first, then the fixes the classification made visible. In-app purchases are about 8% of company orders, which is the scale the rails run at.

Results

+~30%

directly attributable in-app upsell purchases

Measured after the hardening rolled out.

4

payment rails brought under one error model

Braintree, StoreKit 1 and 2, Apple Pay, iOS webview.

Payment failure noise in monitoring measurably dropped after rollout

Qualitative: the drop was observed, the exact percentage was not captured.

What's next

The classification is the base the platform now builds new purchase paths on.

Have a similar problem? Tell us