iOS
iOS works, with two differences from Android, both of which come from the platform rather than from us.
Download the Swift package, unzip it into your project, and add it as a local package:
dependencies: [ .package(path: "vendor/MoneyLanded")]A Git URL for Swift Package Manager will replace this once the repository is public; there is no package registry entry to point at today, and pointing at one that does not exist would be worse than a zip.
There is no system-wide UPI chooser. A upi:// URL has nothing to open, so
you open a specific app’s scheme. Every reserve response carries them:
"app_links": { "phonepe": "phonepe://pay?...", "gpay": "gpay://upi/pay?...", "paytm": "paytmmp://pay?..."}There is no receipt. iOS grants an app nothing back from another app’s
payment flow, so there is no UTR to report and no receipt evidence. iOS
payments confirm on the amount, like everyone else’s. This is a platform
limit, not something a future version fixes.
Reserving and opening
Section titled “Reserving and opening”struct Reservation: Decodable { let paid_paise: Int let discount_paise: Int let vpa: String let upi_uri: String // always present -- the fallback below needs it let app_links: [String: String]}
func reserve(pageToken: String, app: String) async throws -> Reservation { var req = URLRequest(url: URL(string: "https://moneylanded.com/p/\(pageToken)/reserve")!) req.httpMethod = "POST" req.setValue("application/json", forHTTPHeaderField: "Content-Type") req.httpBody = try JSONEncoder().encode(["app": app]) let (data, _) = try await URLSession.shared.data(for: req) return try JSONDecoder().decode(Reservation.self, from: data)}
// `app_links` carries a per-app scheme only for the apps that HAVE one --// phonepe, gpay and paytm. `bhim`, `other` and `qr` are valid values of `app`// with no entry, so a lookup that gives up on a miss silently does nothing when// the customer picks BHIM: they tap, and no app opens. Fall back to `upi_uri`,// the generic `upi://pay?...` link, which every UPI app registers.func open(_ r: Reservation, app: String) { let link = r.app_links[app] ?? r.upi_uri guard let url = URL(string: link) else { return } UIApplication.shared.open(url)}Add the schemes you offer to LSApplicationQueriesSchemes in Info.plist if
you want to check with canOpenURL before showing a button — phonepe,
gpay, paytmmp.
Then poll GET /p/{token}/status until status is confirmed, or let your
server take the payment.confirmed webhook and push to the app.
As on Android: the page token is the only credential in the app. Your API key stays on your server.
The copy-the-UPI-ID fallback, and the one thing to warn about
Section titled “The copy-the-UPI-ID fallback, and the one thing to warn about”Some customers do not have any of the three apps you linked. The reserve
response carries vpa and paid_paise for exactly that case: show the UPI ID,
show the amount, and let them copy both into whatever app they use.
Show this warning next to it, and mean it:
Pay this exact amount. A different amount cannot be confirmed for this order.
This is the one path in the whole product where a customer types an amount by hand, and it is where nearly all our parked credits come from — somebody rounds ₹498.99 up to ₹499. It is recoverable in one click from your unplaced list, and it is worth a sentence of prevention. See Limits.