# Quickstart

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. 

> ⚠ TO CONFIRM: How does a developer get sandbox credentials (seller_payme_id/MPL, payme_client_key, public key) — self-service sign-up, an account manager, or an email to support? [Q5]

## 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 `103`/`106`/`109`/`112` to let the buyer choose up to 3/6/9/12. |
| `sale_callback_url` | Where PayMe sends the server-to-server callback. Defaults to the merchant's settings. |
| `sale_return_url` | Where the buyer is redirected after paying. Defaults to the merchant's settings. |
| `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://preprod.paymeservice.com/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"
}
```



- `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.
- 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/introduction/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. 

> ⚠ TO CONFIRM: The `sale_url` host is most likely `sandbox.payme.io` in the sandbox and `live.payme.io` in production; the `preprod.paymeservice.com` host in the response example above appears to be a pre-production host. Please confirm, so the example can be corrected. [Q38]

To try a decline, pay with `4000000000000002`: the result is HTTP 200 with `status_code: 1` and error `20004`. Other decline cards are on [Environments](/payments/getting-started/environments). 

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&currency=ILS&transaction_id=order-1001&is_token_sale=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](/marketplaces-and-platforms/lists-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`. 

## 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 [Create Payment With Buyer Key (Token)](/marketplaces-and-platforms/payments/create-payment-with-buyer-key-token); to authorize now and capture later, see [Capture sale](/marketplaces-and-platforms/payments/capture-sale).
