# Quickstart Building with an AI agent? Start at https://docs.payme.io/agents.md ## Prerequisites - Sandbox seller_payme_id (MPL) - A public HTTPS URL that can receive POST requests (not localhost or a private IP) ## Related - [Create a sale (hosted payment page)](/payments/hosted-payment-page/post-generate-sale) - [Refund a sale](/payments/post-sale-actions/post-refund-sale) - [Test Cards and Payment Methods](/payments/getting-started/test-cards-and-payment-methods) - [Callbacks](/payments/getting-started/callbacks) - [Errors](/payments/getting-started/errors) Your first sale in the sandbox: you will create a sale, pay it as a buyer, receive PayMe's callback, and refund it. Every field below comes from the endpoint pages linked in each step. **You need:** - a sandbox `seller_payme_id` (MPL) — see [Authentication](/payments/getting-started/authentication). Keep it on your server: on its own it authenticates seller-level calls; - a public URL that accepts `POST` requests, for the callback. It may not be `localhost` or a private IP address; use HTTPS. **Getting sandbox credentials.** If you already have a PayMe account, open the test account from the dashboard: click your name at the top → **Switch to test account** → **Create test account**. A developer with no account registers at → "הרשמה" (Register), then opens the test account from the dashboard as above. The "התנסות" (Try it out) button opens a shared demo, not your own sandbox: it has no keys of your own. The sandbox is free and asks for no signatures. Your keys, your MPL (`seller_payme_id`, with a test/production toggle), both base URLs and the Hosted Fields JSAPI credentials are on one dashboard page: **Admin → API & Integration** (API ואינטגרציה). If that page is missing, install the API service from the dashboard app store (חנות אפליקציות) first; depending on your account it may carry a charge. Partners receive `payme_client_key` from PayMe Partners (); it is never issued to individual sellers. Integration questions: . ## Step 1 — Create the sale Endpoint: [`POST /generate-sale`](/payments/hosted-payment-page/post-generate-sale). No payment happens yet. ```bash 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": "Test order", "transaction_id": "order-1001", "installments": "1", "sale_callback_url": "https://your-server.example.com/payme/callback", "sale_return_url": "https://your-site.example.com/payment/success", "language": "en" }' ``` | Field | Meaning | |-------|---------| | `seller_payme_id` | Your MPL. Required. | | `sale_price` | Price in minor units: `1000` = 10.00. Default allowed range `500`–`4000000`, in every currency (so `500` is 5.00 ILS but 500 JPY). Required. | | `currency` | 3-letter ISO 4217 code, enabled on your account. Required. | | `product_name` | Short product description, up to 500 characters. Required. | | `transaction_id` | Your own order ID, for correlation with PayMe. Optional. | | `installments` | `1`–`12` fixed, or `1NN` (`102`–`112`) to let the buyer choose up to NN, for example `106` for up to 6. Optional, default `1`. More than 12 can be enabled for your account on request. | | `sale_callback_url` | Where PayMe sends the server-to-server callback. 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. | | `sale_return_url` | Where the buyer is redirected after paying. If omitted, your partner's default return URL is used, if one is set. | | `language` | Payment page and error-message language. If omitted, messages are in Hebrew — always send `"en"` while integrating. | `sale_price` and `installments` accept a number or a numeric string (`1000` or `"1000"`, `1` or `"1"`). A non-numeric `sale_price` returns error `352`; a non-numeric `installments` returns error `10`. A decimal `sale_price` is rounded to whole minor units. Successful response (for the request above): ```json { "status_code": 0, "sale_url": "https://sandbox.payme.io/sale/generate/XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX", "payme_sale_id": "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX", "payme_sale_code": 12345678, "price": 1000, "transaction_id": "order-1001", "currency": "ILS", "sale_payment_method": "credit-card" } ``` - `status_code` `0` means success, `1` means error. - Save `payme_sale_id` (PayMe's sale ID) next to your `transaction_id`. You need it to refund. - Responses also carry a `session` value; quote it when you contact PayMe. - If `status_code` is `1`, read `status_error_code` and `status_error_details` — see [Errors](/payments/getting-started/errors). Errors usually come back with HTTP 500, so check `status_code`, not the HTTP status. - PayMe does not de-duplicate `transaction_id`: sending this request twice creates two sales. ## Step 2 — Pay as the buyer Open `sale_url` in a browser (or an IFRAME). Pay with a sandbox test card, for example: | Card number | Expiry | CVV | Social ID | |-------------|--------|-----|-----------| | `5326105300985846` | `12/30` | `658` | `008336174` | This is a local Israeli card that accepts ILS only. Other cards, including cards that force a decline, are on [Test Cards and Payment Methods](/payments/getting-started/test-cards-and-payment-methods). You can pre-fill buyer details by appending query parameters to `sale_url`, for example `?first_name=First&last_name=Last&email=test@example.com`. Card fields cannot be pre-filled. Use `sale_url` exactly as returned. It has no fixed expiry: it works while the sale is `initial`, and after a declined attempt, when the sale is `failed` and a good card can still complete it. To try a decline, pay with `4000000000000002`: the result is HTTP 200 with `status_code: 1` and error `20004`. Other decline cards are on [Test Cards and Payment Methods](/payments/getting-started/test-cards-and-payment-methods#credit-card-numbers-for-testing-specific-responses-and-errors). After a successful payment the buyer is redirected (`GET`) to your `sale_return_url` with the sale details as query parameters: ``` https://your-site.example.com/payment/success?payme_status=success&payme_signature=75e99dbcb25cdfbe1c62f0b9376f4144&payme_sale_id=XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX&payme_transaction_id=XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX&price=1000¤cy=ILS&transaction_id=order-1001&is_token_sale=0&is_foreign_card=0 ``` Use the redirect only to show the buyer a result page. Use the callback (step 3) to update your order. ## Step 3 — Receive the callback When the sale is paid, PayMe sends a `POST` with an `x-www-form-urlencoded` body to `sale_callback_url`. Key fields: | Field | Example | Meaning | |-------|---------|---------| | `notify_type` | `sale-complete` | What happened. See [Callbacks](/payments/getting-started/callbacks). | | `status_code` | `0` | `0` success, `1` error | | `payme_sale_id` | `XXXXXXXX-…` | PayMe's sale ID | | `transaction_id` | `order-1001` | Your order ID | | `sale_status` | `completed` | See [Sale Statuses](/payments/codes-statuses/sale-statuses) | | `price`, `currency` | `1000`, `ILS` | Amount in minor units and currency | | `payme_signature` | `75e99dbcb25cdfbe1c62f0b9376f4144` | Signature | To test your handler before you have a sandbox account, send yourself a callback with the documented shape: ```bash curl -X POST https://your-server.example.com/payme/callback \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'status_code=0' \ --data-urlencode 'notify_type=sale-complete' \ --data-urlencode 'sale_created=2016-01-01 15:15:15' \ --data-urlencode 'transaction_id=order-1001' \ --data-urlencode 'payme_sale_id=XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX' \ --data-urlencode 'payme_sale_code=12345678' \ --data-urlencode 'payme_transaction_id=XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX' \ --data-urlencode 'price=1000' \ --data-urlencode 'currency=ILS' \ --data-urlencode 'sale_status=completed' \ --data-urlencode 'installments=1' \ --data-urlencode 'is_token_sale=0' \ --data-urlencode 'payme_signature=75e99dbcb25cdfbe1c62f0b9376f4144' ``` Your handler should: 1. **Reply `200` within 5 seconds.** Any status below 400 counts as delivered; anything else, or a timeout, makes PayMe retry — up to 3 attempts in total, then the callback is dropped. 2. **Ignore repeats** — de-duplicate on `notify_type` + `payme_sale_id` + `payme_transaction_id`. 3. **Verify `payme_signature`** — the recipe is on [Callbacks](/payments/getting-started/callbacks) (section "Verifying a callback"). 4. **Confirm the sale before fulfilling** — treat the callback's status and amount as a notification, not proof of payment: call [`POST /get-sales`](/payments/query-apis/post-get-sales) with `sale_payme_id` and check `sale_status` and `sale_price`. [`POST /get-transactions`](/payments/query-apis/post-get-transactions) works too, and shows each charge and refund as a row. ## Step 4 — Refund the sale Endpoint: [`POST /refund-sale`](/payments/post-sale-actions/post-refund-sale). Omit `sale_refund_amount` for a full refund; send it for a partial refund (minimum `500`). You can refund several times as long as the total does not exceed the sale amount. ```bash curl -X POST https://sandbox.payme.io/api/refund-sale \ -H 'Content-Type: application/json' \ -d '{ "seller_payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-1234567", "payme_sale_id": "SALEDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "sale_refund_amount": 500, "language": "en" }' ``` `payme_client_key` is not needed here: a merchant can refund with `seller_payme_id` alone. Partners (platforms) may also send their `payme_client_key`, in which case the seller must belong to that partner. Successful response (abridged): ```json { "status_code": 0, "status_error_code": 0, "payme_status": "success", "sale_status": "partial-refund", "sale_refund_buffer": 500, "payme_transaction_total": 500, "payme_transaction_id": "TRANDEMO-XXXXXXXX-XXXXXXXX-XXXXXXXX", "payme_signature": "b71e**************ca5a" } ``` PayMe also sends a callback with `notify_type` `refund` to the callback URL; its `payme_transaction_total` is the amount of this refund and `sale_refund_buffer` the amount still refundable. **Do not blindly retry a refund that timed out**: a repeated partial refund runs again and refunds a second time. Read [Idempotency and retries](/payments/getting-started/idempotency-and-retries) first. ## Next steps - [Callbacks](/payments/getting-started/callbacks) — every notification type and how to handle it. - [Errors](/payments/getting-started/errors) — error codes and messages. - To charge a saved card later, see [Save a card and charge it later](/accepting-payments/capture-and-saved-cards/save-a-card-and-charge-it-later); to authorize now and capture later, see [Capture sale](/accepting-payments/capture-and-saved-cards/capture-sale).