Accept payments in a mobile app

Prerequisites

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

  1. The buyer taps Pay in your app. Your app asks your own server to start a checkout for the order.
  2. Your server calls generate-sale with your MPL and sends the sale_url back to the app.
  3. The app opens sale_url in a secure in-app browser: SFSafariViewController on iOS, Chrome Custom Tabs on Android.
  4. The buyer pays. PayMe sends a callback to your server and redirects the in-app browser to your sale_return_url.
  5. Your return page hands the buyer back to the app, and the app asks your server for the order's status.
  6. Your server confirms the sale with get-sales before 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.

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:

  1. Point sale_return_url at an HTTPS page on your own domain.
  2. 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.
  3. 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

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

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