Accept payments in a mobile app
Prerequisites
- A server that holds your seller_payme_id (MPL) and receives callbacks
- Sandbox credentials (see Authentication)
Related
- Create a sale (hosted payment page)
- Hosted Payment Page options
- Callbacks
- List sales
- Understand the sale lifecycle
- Let the buyer choose how to pay
- Save a card and charge it later
- Hosted Fields JSAPI guide
- Authentication
- Get API access and go live: from sandbox to production
- iOS SDK
- Android SDK
To take payments in an iOS or Android app, create the sale on your server and open PayMe's payment page in a secure in-app browser. The buyer types the card details into PayMe's page, your app holds no PayMe credentials, and your server confirms the result before you deliver the order. This guide shows the flow, the parts that are specific to apps, and what to test.
How it works
- The buyer taps Pay in your app. Your app asks your own server to start a checkout for the order.
- Your server calls
generate-salewith your MPL and sends thesale_urlback to the app. - The app opens
sale_urlin a secure in-app browser:SFSafariViewControlleron iOS, Chrome Custom Tabs on Android. - The buyer pays. PayMe sends a callback to your server and redirects the in-app browser to your
sale_return_url. - Your return page hands the buyer back to the app, and the app asks your server for the order's status.
- Your server confirms the sale with
get-salesbefore it marks the order paid.
The app talks only to your server. Your server is the only part that talks to PayMe.
Step 1: Create the sale on your server
When the app asks for a checkout, your server creates the sale:
curl -X POST https://sandbox.payme.io/api/generate-sale \
-H 'Content-Type: application/json' \
-d '{
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"sale_price": 1000,
"currency": "ILS",
"product_name": "Order 1001",
"transaction_id": "order-1001",
"sale_callback_url": "https://your-server.example.com/payme/callback",
"sale_return_url": "https://your-site.example.com/app/payment-done",
"language": "en"
}'
The response holds sale_url and payme_sale_id. Store payme_sale_id with the order on your server, and send only sale_url to the app.
- Protect your own endpoint. The app calls your server's checkout endpoint with your app's own user authentication. Your server decides the price, never the app.
- One sale per order. If the buyer leaves and comes back to the same unpaid order, reopen the same
sale_urlinstead of creating a new sale. A second sale gives the buyer two payable pages. To close a sale after a set time, sendsale_expiry_seconds. See Understand the sale lifecycle. - Pre-fill the buyer's details with
sale_name,sale_emailandsale_mobile, so the buyer types less on a phone. See Hosted Payment Page options.
Step 2: Open sale_url in a secure in-app browser
| Platform | Open sale_url in |
Not in |
|---|---|---|
| iOS | SFSafariViewController |
WKWebView |
| Android | Chrome Custom Tabs | WebView |
A secure in-app browser runs the page in the system browser, apart from your app: your app can't read or change the page, and the buyer can see its address. In a WebView, your app controls the page and could read what the buyer types, so card details would pass through your app.
With a cross-platform framework such as Flutter or React Native, use a plugin that opens these same system browsers, not one that embeds a WebView.
Open sale_url exactly as returned. You can add the pre-fill query parameters from Hosted Payment Page options.
Step 3: Bring the buyer back to the app
After a successful payment, PayMe redirects the in-app browser to your sale_return_url, with the sale details as query parameters. See Success redirect.
sale_return_url must be an http:// or https:// address with a public host. A custom app scheme, such as myapp://payment-done, fails the whole generate-sale call with error 21. See Callback URL rules. So:
- Point
sale_return_urlat an HTTPS page on your own domain. - From that page, hand the buyer back to the app. Register the page's address as a Universal Link (iOS) or an App Link (Android), so the system can open your app for it. Also show a Return to the app button on the page that opens your app.
- Keep the page useful on its own: it shows the result and the button. Whether a redirect opens your app directly depends on the operating system, the browser and your app's link setup, so test it on real devices.
The buyer can also close the in-app browser without paying. Then there is no redirect. Handle that case too: on iOS, SFSafariViewControllerDelegate calls safariViewControllerDidFinish(_:); on Android, your activity resumes. In both cases, ask your server for the order's status.
Use the query parameters only to choose what the app shows next. The order's status comes from your server.
Step 4: Confirm the payment on your server
PayMe sends a sale-complete callback to your sale_callback_url: a form-encoded POST. Reply 200 within 5 seconds. See Callbacks.
Before you deliver, confirm the sale. Call get-sales with the payme_sale_id you stored:
curl -X POST https://sandbox.payme.io/api/get-sales \
-H 'Content-Type: application/json' \
-d '{
"seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567",
"sale_payme_id": "SALE1767-704312AB-CD3FGH12-JKLM4567"
}'
Check that the sale in items has sale_status completed and the sale_price of your order. Then mark the order paid. When the app asks for the order's status, answer from your own records.
If the app asks before the callback arrives, your server can call get-sales itself. By default get-sales leaves out unpaid sales (status initial), so include initial in sale_status, for example "sale_status": ["initial", "completed", "failed"]. A failed sale isn't final: the buyer can still pay it on the same page. See Understand the sale lifecycle.
Partners also verify payme_signature on the callback. A seller without a partner key relies on the get-sales check above.
Payment methods inside an in-app browser
- Apple Pay and Google Pay appear on PayMe's page only when the buyer's browser can use them. Inside an in-app browser, that depends on the device, the operating system version and the browser. Test each wallet in your app on real devices before you offer it, and keep the card as the fallback. See Let the buyer choose how to pay.
- Bit and 3D Secure can take the buyer to another app or to the bank's page, and back. Test that the buyer returns to the in-app browser and finishes the payment.
Save a card for faster checkouts
The same flow can save the buyer's card: create the sale with capture_buyer (charge and save) or sale_type token (save only). Later charges run from your server with the buyer_key, with no payment page and nothing to open in the app. See Save a card and charge it later.
Your own checkout design
If you need your own checkout design, build it as a web page on your domain with Hosted Fields, and open that page in the in-app browser in the same way. The page uses the public key only, and your server charges the token and confirms the result. Wallets on your own page need their own setup: see Accept Apple Pay and Accept Google Pay.
Keep credentials out of the app
- Never put
seller_payme_id(the MPL),payme_client_keyor the client password in your app, its configuration or its network calls. Anyone can unpack an app, and the MPL alone authenticates calls such as refunds. See Authentication. - The flow on this page needs no PayMe key in the app at all.
- The callback goes to your server, never to the app.
The legacy iOS and Android SDKs
PayMe's iOS SDK and Android SDK are legacy. They collect card details inside your app and put your MPL in it, so they aren't recommended for new apps. Their pages stay available for existing integrations. Build new apps with the flow on this page.
Before you release
- The app calls only your server. No PayMe credential is in the app.
-
sale_urlopens inSFSafariViewControlleror Chrome Custom Tabs, not in a WebView. -
sale_return_urlis an HTTPS page on your domain that leads back to the app, tested on iOS and Android devices. - The app handles a buyer who closes the browser without paying.
- Your server marks an order paid only after the callback and a
get-salescheck. - Each payment method you offer works in the in-app browser on real devices.
- Production calls use
https://live.payme.io/apiand your production MPL. See Get API access and go live.