# Create a sale (hosted payment page) Building with an AI agent? Start at https://docs.payme.io/agents.md - Sandbox: `POST https://sandbox.payme.io/api/generate-sale` - Production: `POST https://live.payme.io/api/generate-sale` ## Credentials - Request body field `seller_payme_id` (required): The seller's MPL. Send it in the JSON body: `generate-sale` doesn't read the `PayMe-Merchant-Key` header, so a request without a body MPL returns error `1`. Partners may send `seller_id` with `payme_client_key` instead. - Request body field `payme_client_key` (optional): Partners only: your partner key (or send the `PayMe-Partner-Key` header). With `seller_payme_id`, the seller must belong to you, otherwise error `20`. ## Overview Creates a sale and returns `sale_url`, PayMe's payment page for it. Show `sale_url` to the buyer in an iframe or a redirect. PayMe sends the result to `sale_callback_url` and redirects the buyer to `sale_return_url`. `POST https://sandbox.payme.io/api/generate-sale` (production: `https://live.payme.io/api/generate-sale`), with a JSON body (`Content-Type: application/json`). | To | Send | |---|---| | Charge now (J4) | `sale_type: sale` (default) | | Authorize now, capture later (J5, up to 168 hours) | `sale_type: authorize`; then [capture-sale](/accepting-payments/capture-and-saved-cards/capture-sale) | | Let many buyers pay one link | `sale_type: template` ([Template sales](/accepting-payments/checkout-and-in-person/multi-payment-page)) | | Save the card while charging | `capture_buyer: 1` | | Save the card without charging | `sale_type: token` ([Save a card](/payments/tokenization/post-generate-sale-tokenization)) | | Charge a saved card now (no page) | `buyer_key` ([Save a card and charge it later](/accepting-payments/capture-and-saved-cards/save-a-card-and-charge-it-later)) | | Offer every payment method on one page | `sale_payment_method: multi` ([Multi-method checkout](/payments/hosted-payment-page/post-generate-sale-2)) | All sale types are on [Sale Types](/payments/codes-statuses/sale-types), and all payment methods on [Available Payment Methods](/payments/wallets-apms). ### Credentials Send `seller_payme_id` (the MPL) in the JSON body: this endpoint doesn't read the `PayMe-Merchant-Key` header, so a request without a body MPL returns error `1`. Partners add `payme_client_key` (or the `PayMe-Partner-Key` header) and may name the seller by `seller_id` instead. See [Authentication](/payments/getting-started/authentication). ### Rules for every request - Amounts are in minor units (`1000` = 10.00). - Never put card numbers in any field. A value (other than `transaction_id` and `order_number`) that contains a 14–19-digit number passing the card checksum is refused with error `20` (HTTP 400), " contains invalid data". - Don't send the legacy `price` field: it is in major units and, when numeric, overrides `sale_price`. - PayMe doesn't de-duplicate `transaction_id`: every call creates a new sale. See [Idempotency and Retries](/payments/getting-started/idempotency-and-retries). - Errors return `status_code: 1` with `status_error_code`, most with HTTP 500. See [Errors](/payments/getting-started/errors). ### Callbacks When the sale is paid, PayMe sends a `POST` with an `application/x-www-form-urlencoded` body to `sale_callback_url`. The notification types and fields are on [Callbacks](/payments/getting-started/callbacks). ### `sale_url` validity and embedding `sale_url` has no fixed expiry. The payment page stays usable while the sale is unpaid: `initial`, or `failed` after a declined attempt. A good card on the same page then completes a `failed` sale, so `failed` isn't final, and opening a second sale gives the buyer two payable pages. The same rule applies to card, Bit, Apple Pay and Google Pay sales. For a hard deadline, send `sale_expiry_seconds` when you create the sale: it closes a `failed` page too. An ordinary open sale has no cancel call. `sale_url` can be shown inside an iframe, 3D Secure included. Some issuing banks block their own 3D Secure screen inside an iframe, so keep a fallback that opens the page in a full window. The page reports completion with a `postMessage` to the parent window and by the redirect to `sale_return_url`. ### Charging a saved card (`buyer_key`) With `buyer_key`, the call charges the token at once: there is no payment page and no `sale_url`. On success the response holds the sale and transaction fields, the same as the callback. A declined charge also returns **HTTP 200**, with `status_code: 1`, so always check `status_code`. Each call creates a new sale, even when the charge is refused. The token's own payment method is used, whatever `sale_payment_method` you send. Errors: a token of another seller returns `605`; an inactive or already-used single-use token returns `604`; a charge over the seller's processing limit returns `130`. ### Issuing an invoice With the invoicing service active on the account, a paid sale issues a document and a refund automatically issues a credit document. To skip the document for one sale, send `"services": [{"name": "invoice", "settings": {"active": false}}]` in the request; dashboard and payment-link sales keep issuing. When you charge a saved card (`buyer_key`) and the invoicing service is active, you can issue the invoice to a business: send `buyer_business_name` (the business name) and `buyer_business_code` (its VAT identifier). Send both, or neither. ## Parameters This endpoint takes no path, query or header parameters. ## Request body Content type: `application/json` | Field | Type | Required | Description | | --- | --- | --- | --- | | `seller_payme_id` | string | yes | The seller's MPL. Send it in the JSON body: `generate-sale` doesn't read the `PayMe-Merchant-Key` header, so a request without a body MPL returns error `1`. Partners may send `seller_id` with `payme_client_key` instead. | | `payme_client_key` | string | no | Partners only: your partner key (or send the `PayMe-Partner-Key` header). With `seller_payme_id`, the seller must belong to you, otherwise error `20`. | | `seller_id` | string | no | Partners only: your own ID for the seller, set in `create-seller`, sent instead of `seller_payme_id`. Requires `payme_client_key`. | | `sale_price` | integer | yes | Sale amount in minor units: 50.75 is sent as `5075`. The default allowed range is `500`–`4000000`, the same in every currency; PayMe can set a different range for a partner. A missing, non-numeric or out-of-range value returns error `352`. Decimals are rounded. | | `currency` | string | yes | Sale currency, a 3-letter [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code such as `ILS`, enabled on the account (otherwise error `252`). Required for `sale` and `authorize`; a `token` or `template` sale without it uses the account's default currency. | | `product_name` | string | yes | Short description of the product or service, up to 500 characters. Shown to the buyer, and on the invoice when the invoicing service is active. Missing returns error `353`. | | `transaction_id` | string | no | Your own order reference, up to 50 characters. PayMe returns it in responses and callbacks but doesn't check it for uniqueness. | | `installments` | string | no | Number of payments. `1` (default) or `0`: one payment. `2`–`12`: that fixed number. `102`–`112`: the buyer chooses on the page, up to the last two digits (`106` = up to 6). More than one installment is ILS only (`312`) and works only with `credit-card` or `multi` (other methods return `351`). On a `multi` page that lets the buyer choose, a buyer who pays with Apple Pay or Google Pay pays in one payment. Fixed values above 12 are accepted when the sale is created, but the payment fails unless PayMe has enabled more installments on your account. A non-numeric value returns `10`, any other unaccepted value `351`, and an ILS price lower than the number of installments `358`. | | `sale_type` | string | no | `sale` (default) – charge now (J4). `authorize` – reserve the amount for up to 168 hours, then capture it with [`capture-sale`](/accepting-payments/capture-and-saved-cards/capture-sale) (J5). `token` – save the card without charging (the price is set to `100`). `template` – one link that many buyers can pay. `credit` – pay money to the card (needs the Credit Sale service). `multi-capture` – capture part of an authorization (needs `origin_sale_id`). The full list is on [Sale Types](/payments/codes-statuses/sale-types); any other value returns `357`. | | `sale_payment_method` | string | no | Payment method; see [Available Payment Methods](/payments/wallets-apms). Default `credit-card`. Send `multi` to let the buyer choose among every method enabled on the account. Case-insensitive. An unknown value returns `24`; a method not enabled on the account returns `720` with the method name; `bacs` needs `GBP` and `sepa` needs `EUR` (`320`). Send a string: an array returns `368` (HTTP 400). | | `market_fee` | number | no | The partner's (marketplace's) fee: a percentage of the sale price, VAT included, from `0` to `60`. It is added on top of PayMe's fees and transferred to the marketplace once a month. Default: the seller's market fee set at `create-seller`. A non-numeric value returns `10`, a negative one `304`, and more than 60 `308`. Ignored for sellers paid directly. | | `sale_callback_url` | string | no | Where PayMe sends server-to-server callbacks for this sale, as a form-encoded `POST`; see [Callbacks](/payments/getting-started/callbacks). If omitted, PayMe uses your partner's default callback URL, if one is set; a seller has no dashboard setting for it, so send it on every call. Must be a public address: `localhost`, private or loopback IP addresses and IPv6 addresses are refused with error `21`, which fails the whole call. | | `sale_return_url` | string | no | Where PayMe redirects the buyer (the iframe or the full page) after a successful payment, with the sale details as query parameters; see [Hosted Payment Page options](/accepting-payments/checkout-and-in-person/hosted-payment-page-options). If omitted, your partner's default return URL is used, if one is set. Same address rules as `sale_callback_url` (error `21`). | | `cancel_url` | string | no | Where to send the buyer when 3D Secure authentication fails, instead of back to the payment page. Also the PayPal cancel address. Must be a public URL (error `21`). | | `sale_send_notification` | boolean | no | `true`: PayMe sends the buyer a payment request with the payment link, by email to `sale_email` and by SMS to `sale_mobile`. Default `false`. | | `sale_email` | string | no | Buyer's email. Pre-fills the payment page and receives the payment request when `sale_send_notification` is `true`. | | `sale_mobile` | string | no | Buyer's mobile number. Pre-fills the payment page and receives the SMS payment request when `sale_send_notification` is `true` and the SMS service is active on the account. | | `sale_name` | string | no | Buyer's full name. Pre-fills the payment page (split at the first space into first and last name) and is used in notifications. | | `sale_send_creation_callback` | boolean | no | `true`: PayMe also sends a `sale-create` callback, with `sale_url`, as soon as the sale is created. Default `false`. | | `sale_expiry_seconds` | integer | no | How many seconds the sale stays payable after it is created. After that, the payment page answers HTTP 403 and paying the sale fails with error `367`. Without it, `sale_url` has no fixed expiry. | | `capture_buyer` | string | no | `1` or `true` to save the buyer's card as a token after the payment; the token arrives as `buyer_key` in the callback. Omit it, or send `0`, to skip. Works with `credit-card`, `apple-pay`, `il-direct-debit` and `multi` (other methods return `4`). Can't be combined with `buyer_key` (`601`). | | `buyer_is_private` | boolean | no | With `capture_buyer` or a token sale: `true` ties the token to this seller, so `get-buyer-key` works with the seller's MPL. Without it the token is account-level: any seller of the same partner can charge it, and a `get-buyer-key` call that sends `seller_payme_id` fails with error `603`. | | `buyer_perform_validation` | boolean | no | With `capture_buyer` or a token sale: whether PayMe validates the card with the issuer before saving the token. Default `true`. Sending `true` without `capture_buyer` or a token sale returns `608`. | | `buyer_key` | string | no | Charges a saved token at once: no payment page and no `sale_url` (see the "Token charged" response). The token comes from a callback, [`get-buyer-key`](/payments/tokenization/post-get-buyer-key) or Hosted Fields `tokenize()`. Can't be combined with `capture_buyer` (`601`), `sale_type` `token`/`seller-token` (`602`) or `template` (`606`). | | `buyer_business_name` | string | no | With `buyer_key` and the invoicing service: the business name the invoice is issued to. Send it together with `buyer_business_code`, or neither. | | `buyer_business_code` | integer | no | With `buyer_key` and the invoicing service: the business code (VAT identifier) the invoice is issued to. Send it together with `buyer_business_name`, or neither. | | `layout` | string | no | Payment page layout: `dynamic` (default), `dynamic-loose` (no social ID or CVV validation), `qr-sms` (Bit: QR code or SMS), `token-capture` (save a card without a charge; requires `sale_type: token`). Other values fall back to `dynamic`. | | `sale_should_be_opened_once` | boolean | no | `true`: with `layout: token-capture`, the page can be opened only once. Ignored otherwise. | | `language` | string | no | Language of the payment page and the error messages. Default `he`. `he` and `en` are fully supported; `es`, `ru` and `ar` are accepted, but messages and the payment page are then mostly in English. Case-insensitive; any other value, or an empty string, returns error `51`. | | `services` | array | no | Per-sale settings for services on the account, as objects `{"name": "", "settings": {...}}`. For example `[{"name": "invoice", "settings": {"active": false}}]` skips the invoice for this sale, and `[{"name": "3DSecure", "settings": {"active": true}}]` forces 3D Secure. Each object needs `settings` (error `1`). Unknown or inactive services are ignored. | | `sale_deferred_months` | integer | no | Deferred charge: the number of months, `1`–`12`, by which the card charge is deferred. Only for `credit-card` sales of `sale_type` `sale` (other types or methods return `4`); a value outside `1`–`12` returns `365`. PayMe must enable deferred charges on your account. | | `order_number` | string | no | Purchase order number, used for level 2 and level 3 card data. Up to 15 digits. | | `items` | array | no | Line items of the sale, as an array of objects. If you send `items`, every item needs `name`, `quantity`, `unit_price` and `total`; an invalid item returns error `20` (HTTP 422) with the failing rules in `status_additional_info`. Item keys are case-insensitive. | | `items[].name` | string | yes | Item name, 1–50 characters. | | `items[].quantity` | integer | yes | Quantity, `0`–`999999`. | | `items[].unit_price` | integer | yes | Price of one unit, in minor units. | | `items[].total` | integer | yes | Line total after discount, in minor units, `0`–`9000000000`. | | `items[].unit_measurement` | string | no | Unit of measurement, for example `kg`. | | `items[].discount_total` | number | no | Discount on this line, in minor units, sent as a negative number. | | `items[].description` | string | no | Item description, up to 150 characters. | | `items[].product_code` | string | no | Your catalog code for the item, up to 100 characters. | | `items[].commodity_code` | number | no | Numeric commodity code of the item. | | `items[].custom1` | string | no | Free text for your own use, up to 100 characters. | | `items[].custom2` | string | no | Free text for your own use, up to 100 characters. | | `fees` | object | no | Amounts included in the payment, for level 2 and level 3 card data. Each is in minor units, up to 12 digits. | | `fees.tax` | number | no | Tax included in the payment. | | `fees.duty` | number | no | International duties included in the payment. | | `fees.discount` | number | no | Total discount given on the payment. | | `fees.shipping` | number | no | Shipping cost included in the payment. | | `shipping_details` | object | no | Where the order is shipped. Stored with the sale. | | `shipping_details.name` | string | no | Addressee's full name or company name, up to 50 characters. | | `shipping_details.email` | string | no | Addressee's email, up to 100 characters. | | `shipping_details.phone` | string | no | Addressee's phone number, up to 50 characters. | | `shipping_details.address` | object | no | The shipping address. | | `shipping_details.address.line1` | string | no | First line of the shipping address. | | `shipping_details.address.line2` | string | no | Second line of the shipping address. | | `shipping_details.address.postal_code` | string | no | Postal code of the shipping address. | | `shipping_details.address.city` | string | no | City of the shipping address. | | `shipping_details.address.state` | string | no | State or region of the shipping address. | | `shipping_details.address.country` | string | no | Two-letter ISO 3166-1 country code, for example `IL`. Other values are ignored. | | `billing_details` | object | no | The payer's billing details. Stored with the sale. | | `billing_details.name` | string | no | Payer's full name or company name. | | `billing_details.email` | string | no | Payer's email. | | `billing_details.phone` | string | no | Payer's phone number. | | `billing_details.address` | object | no | The billing address. | | `billing_details.address.line1` | string | no | First line of the billing address. | | `billing_details.address.line2` | string | no | Second line of the billing address. | | `billing_details.address.postal_code` | string | no | Postal code of the billing address. | | `billing_details.address.city` | string | no | City of the billing address. | | `billing_details.address.state` | string | no | State or region of the billing address. | | `billing_details.address.country` | string | no | Two-letter ISO 3166-1 country code, for example `IL`. Other values are ignored. | ### Request example ```json { "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "sale_price": 1000, "currency": "ILS", "product_name": "Smartphone", "transaction_id": "order-1001", "installments": "1", "sale_type": "sale", "sale_payment_method": "credit-card", "sale_callback_url": "https://your-server.example.com/payme/callback", "sale_return_url": "https://your-site.example.com/payment/success", "sale_name": "First Last", "sale_email": "buyer@example.com", "sale_mobile": "+972500000040", "language": "en" } ``` ## Code samples ### curl ```bash curl -X POST 'https://sandbox.payme.io/api/generate-sale' \ -H 'Content-Type: application/json' \ --data-raw '{ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "sale_price": 1000, "currency": "ILS", "product_name": "Smartphone", "transaction_id": "order-1001", "installments": "1", "sale_type": "sale", "sale_payment_method": "credit-card", "sale_callback_url": "https://your-server.example.com/payme/callback", "sale_return_url": "https://your-site.example.com/payment/success", "sale_name": "First Last", "sale_email": "buyer@example.com", "sale_mobile": "+972500000040", "language": "en" }' ``` ### Node.js ```javascript const response = await fetch("https://sandbox.payme.io/api/generate-sale", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "sale_price": 1000, "currency": "ILS", "product_name": "Smartphone", "transaction_id": "order-1001", "installments": "1", "sale_type": "sale", "sale_payment_method": "credit-card", "sale_callback_url": "https://your-server.example.com/payme/callback", "sale_return_url": "https://your-site.example.com/payment/success", "sale_name": "First Last", "sale_email": "buyer@example.com", "sale_mobile": "+972500000040", "language": "en" }), }); console.log(await response.text()); ``` ### Python ```python import requests response = requests.post( "https://sandbox.payme.io/api/generate-sale", json={ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "sale_price": 1000, "currency": "ILS", "product_name": "Smartphone", "transaction_id": "order-1001", "installments": "1", "sale_type": "sale", "sale_payment_method": "credit-card", "sale_callback_url": "https://your-server.example.com/payme/callback", "sale_return_url": "https://your-site.example.com/payment/success", "sale_name": "First Last", "sale_email": "buyer@example.com", "sale_mobile": "+972500000040", "language": "en", }, ) print(response.text) ``` ### PHP ```php 'POST', CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', ], CURLOPT_POSTFIELDS => $body, ]); $response = curl_exec($ch); curl_close($ch); echo $response; ``` ## Responses ### 200 — Sale created (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0` success, `1` error. | | `sale_url` | string | PayMe's payment page for this sale. Show it to the buyer in an iframe or a redirect, exactly as returned. It stays payable while the sale is `initial` or `failed`, until `sale_expiry_seconds` passes if you sent it. | | `payme_sale_id` | string | PayMe's sale ID. Store it: refunds, captures and queries use it. | | `payme_sale_code` | integer | Short numeric sale code, for display only. | | `price` | integer | Sale amount in minor units (`100` for a token sale). | | `transaction_id` | string | Your order reference, as sent. | | `currency` | string | Sale currency (ISO 4217). | | `sale_payment_method` | string | Payment method of the sale, for example `credit-card` or `multi`. | | `session` | string | Request ID. Quote it when you contact PayMe. | ```json { "status_code": 0, "session": "AtsJ0as5", "sale_url": "https://sandbox.payme.io/sale/generate/SALE1700-000000AA-EXAMPLE1-SALE0001", "payme_sale_id": "SALE1700-000000AA-EXAMPLE1-SALE0001", "payme_sale_code": 12345678, "price": 1000, "transaction_id": "order-1001", "currency": "ILS", "sale_payment_method": "credit-card" } ``` ### 200 — Token charged (`buyer_key`). The response holds the sale and transaction fields, the same as the callback, and has no `sale_url`. A declined charge also returns HTTP 200, with `status_code: 1`, `status_error_code` and `status_error_details`: always check `status_code`. (success) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `0` the token was charged; `1` the charge failed (a decline also comes with HTTP 200). | | `payme_status` | string | `success` or `error`. | | `status_error_code` | integer | `0` on success; otherwise the error or decline code, see [Errors](/payments/getting-started/errors). | | `payme_sale_id` | string | PayMe's sale ID. Every call creates a new sale, even when the charge is refused. | | `payme_sale_code` | integer | Short numeric sale code, for display only. | | `sale_status` | string | See [Sale Statuses](/payments/codes-statuses/sale-statuses): `completed` for a charge, `authorized` for `sale_type: authorize`. | | `sale_type` | string | Sale type name, for example `Sale`; see [Sale Types](/payments/codes-statuses/sale-types). | | `sale_payment_method` | string | The token's payment method; it replaces any `sale_payment_method` you sent. | | `price` | integer | Sale amount in minor units. | | `currency` | string | Sale currency (ISO 4217). | | `transaction_id` | string | Your order reference, as sent. | | `is_token_sale` | boolean | `false`: this charge isn't a token-saving sale. | | `payme_signature` | string | Signature, computed as described on [Callbacks](/payments/getting-started/callbacks). | | `payme_transaction_id` | string | PayMe's ID for this charge. | | `payme_transaction_total` | integer | Amount of this transaction, in minor units. | | `payme_transaction_auth_number` | string | Authorization number from the card company. | | `buyer_card_mask` | string | First 6 and last 4 digits of the card. | | `installments` | integer | Number of installments. | | `sale_paid_date` | string | When the payment happened. | | `sale_3ds` | boolean | Whether 3D Secure was used. | ```json { "status_code": 0, "payme_status": "success", "status_error_code": 0, "payme_sale_id": "SALE1700-000000AA-EXAMPLE1-SALE0001", "payme_sale_code": 12345678, "sale_status": "completed", "sale_type": "Sale", "sale_payment_method": "credit-card", "price": 1000, "currency": "ILS", "transaction_id": "order-1001", "is_token_sale": false, "payme_signature": "f8ffc5bb45fe6c3ad326c439f1a21c28", "payme_transaction_id": "TRAN1700-000000AA-EXAMPLE1-TXN00001", "payme_transaction_total": 1000, "payme_transaction_auth_number": "0123456", "buyer_card_mask": "455743******3431", "installments": 1, "sale_paid_date": "2026-11-09 10:15:02", "sale_3ds": false } ``` ### 500 — Request refused. Most request errors return HTTP 500 with `status_code: 1`; some return 400, 403, 422 or 429. See [Errors](/payments/getting-started/errors). (error) | Field | Type | Description | | --- | --- | --- | | `status_code` | integer | `1` for an error. | | `status_error_code` | integer | PayMe's error code, for example `251` (seller not found) or `352` (invalid price). | | `status_error_details` | string | The error message, in the requested `language` (Hebrew by default). | | `status_additional_info` | string | Extra detail, usually the name of the field at fault. | | `session` | string | Request ID. Quote it when you contact PayMe. | ```json { "status_code": 1, "status_error_details": "Seller was not found", "status_additional_info": "seller_payme_id", "status_error_code": 251, "session": "AtsJ0as5" } ```