Skip to main content

Run the sample app

The SDK's repository ships a SwiftUI demo, Demo/Demo.xcodeproj, that exercises the SDK end to end: card, Apple Pay, PayPal, Klarna, redirection methods, gift cards and co-payment, saved cards, BNPL simulation, session refresh, teardown, and the confirmation of a payment after a redirect. It is the reference to read next to the guides.

It opens on a mode picker with two modes, built on one controller and one session-minting path:

ModeWhat it is
PlaygroundSession → pay → result. Raw session and transaction dumps, an event log, a widget.data paste box, a manual capture tool. For working on the SDK
CheckoutBasket → payment → confirmation. What a shopper sees, with the same SDK calls and none of the developer chrome

Get it​

git clone https://github.com/UpStreamPay/purse-headless-checkout-ios.git
cd purse-headless-checkout-ios
open Demo/Demo.xcodeproj

The demo depends on the SDK targets of the repository, so it always runs against the checked-out version; check out a release tag to match the version you depend on. The package targets iOS only: build for a simulator destination, never swift build.

Give it a session​

The demo mints its sessions through alfred, the usp-widget development server that holds the server-side Purse credentials a real merchant backend would. ALFRED_BASE_URL in .env points at it; blank falls back to http://localhost:9001.

cd ../usp-widget && npm run dev:alfred # port 9001
cp .env.example .env # then set ALFRED_BASE_URL
source scripts/load-env.sh # must be sourced — see the script for why

localhost is correct here: the Simulator shares the host's network stack, where an Android emulator needs 10.0.2.2. A physical device needs your machine's LAN IP.

alfred is internal to Purse

It does nothing outside Purse's network. Merchants create a session with their own sandbox credentials as in Your first payment with Purse, then paste the widget.data string into the playground's session screen. There is no offline path otherwise.

What each screen shows​

ScreenExercises
Session (playground)PurseHeadlessCheckout(widgetData:), the decoded session, the method list with each item's disabled state
Pay (playground)One row per primary method and saved token, the payment element of the selected one, gift card entry with balance check and slices, the split and isPaymentFulfilled, setSession(_:) as "refresh session", BNPL simulation preview
Result (playground)The four PaymentOutcomes, and a Get transactions call against alfred to settle an unconfirmed return
Basket → Payment → Confirmation (checkout)The same SDK calls a merchant ships, ending on a confirmation screen that reads the session's transactions through alfred, polling while they are pending

Both result screens read the session through alfred, which stands in for the merchant backend that holds server-side Purse credentials. Treat that part as backend code, not SDK surface.

Which methods appear​

The session decides. The demo installs all three partner modules at start-up, so any method your entity offers is usable: a card vaulted by Purse, Apple Pay, PayPal, Klarna, any method configured in redirection mode, and every gift card issuer the SDK ships.

Apple Pay needs what this repository cannot ship: the In-App Payments entitlement alongside its merchant identifier, so it only works on a signed build and a real device. The button renders regardless and the failure is reported, which is the honest result. Klarna needs nothing from the app: its client token comes back in the /init answer, so activating Klarna on the entity is enough. Everything else depends only on what the session offers.

Where to look in the code​

FileShows
Demo/Sources/DemoController.swiftBuilding the checkout, installing the partner modules, selecting items, one listener per item, submitting, mapping outcomes, teardown()
Demo/Sources/CheckoutPayScreen.swiftThe shopper-facing pay screen: method rows, the selected element, gift cards, the split
Demo/Sources/SimulationPreview.swiftA complete rendering of simulateLoan
Demo/Sources/ConfirmationScreen.swiftThe order confirmation, from the session's transactions read through alfred (DemoController.confirmPayment())
Demo/Sources/AlfredClient.swiftWhat a merchant backend does, and therefore what is not SDK surface

Two launch arguments help smoke tests and are never on by default: -demo-autostart mints a session and opens the playground's pay screen, and -demo-checkout opens the shopper flow at its basket (with both, it mints and lands on the payment page). CI builds the demo in Debug and Release.