Callbacks
Related
A callback (PayMe also calls it an "IPN") is a request PayMe's server sends to yours when something happens to a sale, a subscription or a seller.
In short: reply quickly with HTTP 200, expect repeats and out-of-order delivery, verify payme_signature, and confirm the sale's status and amount with POST /get-sales before you fulfil an order.
Format
- Method and encoding:
POSTwith anx-www-form-urlencodedbody — not JSON. - Destination: the
sale_callback_urlyou sent. If you omit it, PayMe uses your partner's default callback URL, if your partner has one; a seller has no dashboard setting for it, so always sendsale_callback_url(andsub_callback_urlfor subscriptions). One sale can notify two URLs: the sale's callback URL and the partner's sale callback URL (one delivery per host). - The
languagefield of the request sets the language of the callback.
Callback URL rules
http://andhttps://are both accepted. Use HTTPS.- Non-standard ports are allowed (for example
:8443). - Rejected, with error
21for the whole call:localhostand other hosts without a dot, IP addresses in private or loopback ranges, all IPv6 addresses, and hosts whose top-level domain isn't letters only. Ports must be 2–5 digits. - PayMe doesn't resolve the host, so a valid-looking name is accepted even if it doesn't exist.
sale_return_urlfollows the same rules.
The callback is separate from the return URL redirect. The return URL is a browser GET that takes the buyer to your result page; the callback is server-to-server.
Sale notification types (notify_type)
Every notify_type PayMe sends, with when it fires and its key fields, is listed on Callback types reference.
notify_type |
Meaning |
|---|---|
sale-complete |
The sale was paid successfully |
sale-authorized |
The sale was authorized successfully |
refund |
The sale was refunded — full refund, partial refund, or void of an authorization |
sale-failure |
A payment attempt failed |
sale-chargeback |
The sale was charged back |
sale-chargeback-refund |
The chargeback was reverted |
sale-create |
The sale was created. Sent only when generate-sale included sale_send_creation_callback: true; carries sale_url. |
sale-paid |
Israeli direct debit only: the debit was submitted to the bank. |
sale-il-direct-debit-update |
Israeli direct debit only: the bank authorization status changed. |
sale-failureis sent once per failed attempt, not once per sale. A buyer can retry on the payment page; each failure sends anothersale-failurewith a differentpayme_transaction_id. They stop once the sale completes. Sofailedisn't final: the sale can still move tocompleted, orauthorizedfor a capture sale.- Refund callbacks:
payme_transaction_totalis the amount of this refund,priceis the original sale price,sale_refund_bufferis the amount still refundable, andpayme_transaction_id/payme_signaturerefer to the refund transaction.
After a partial refund sale_status is partial-refund. A sale fully refunded in parts stays partial-refund and never moves to refunded, so track the refunded total yourself.
Sale callback payload
| Field | Example | Description |
|---|---|---|
status_code |
0 |
0 success, 1 error |
status_error_code |
In case of an error, PayMe's error code | |
status_error_details |
In case of an error, the error message | |
notify_type |
sale-complete |
Notification type (table above) |
sale_created |
2016-01-01 15:15:15 |
Sale creation time |
transaction_id |
12345 |
Your order ID |
payme_sale_id |
XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX |
PayMe's sale ID |
payme_sale_code |
12345678 |
Sale code, for display only |
payme_transaction_id |
XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX |
PayMe's ID for this transaction (payment, capture, refund or failed attempt) |
price |
1000 |
Amount in minor units (50.75 → 5075) |
currency |
USD |
ISO 4217 code |
sale_status |
completed |
See Sale Statuses |
payme_transaction_card_brand |
Visa |
Card brand |
payme_transaction_auth_number |
01A2B3C |
Authorization number from the card company |
buyer_card_mask |
458045******4580 |
Masked card number |
buyer_card_exp |
0118 |
Card expiry |
buyer_name, buyer_email, buyer_phone, buyer_social_id |
Buyer details | |
buyer_key |
XXXXXXXX-… |
Token for future payments; only when capture_buyer was set |
installments |
1 |
Number of installments |
sale_paid_date |
2016-01-01 15:16:15 |
When the payment happened (not when the callback was sent) |
sale_release_date |
2016-01-08 15:15:15 |
Release date |
is_token_sale |
0 |
0 false, 1 true |
payme_signature |
75e99dbcb25cdfbe1c62f0b9376f4144 |
Signature (see below) |
sale_invoice_url |
https://www.example.com/XXXXXX.pdf |
Invoice URL, if the seller enabled invoices |
sale_payment_method |
credit-card |
Payment method name; see Available Payment Methods |
sale_type |
Sale |
Sale type name: Sale (also for authorizations), Token, SaleFromTemplate, SaleFromSubscription, … See Sale Types |
payme_transaction_total |
1000 |
Amount of this transaction, in minor units |
buyer_card_is_foreign |
0 |
1 when the card was issued outside Israel |
sale_3ds |
0 |
1 when the payment went through 3D Secure |
buyer_status |
1 |
With buyer_key: the status of the saved token |
Fields with no value are left out of the body.
Other callbacks
Subscriptions
Sent to sub_callback_url; if it is empty, to your partner's default callback URL (merchant_callback_url), if one is set. Subscription callbacks carry no payme_signature.
notify_type |
When |
|---|---|
sub-create |
The subscription was created |
sub-active |
The subscription became active |
sub-iteration-success |
A charge succeeded: first, regular or after a retry, with an identical body in every case (adds payme_sale_id and payme_sale_code; the charge time is sub_prev_date) |
sub-iteration-skipped |
A billing iteration was skipped |
sub-failure |
A payment attempt failed. If the card-validation transaction failed when the buyer paid the subscription, its status is 4 and PayMe does not retry. If a scheduled charge failed and will be retried, its status is 7 and sub_next_date moves one day. Carries sub_error_text, but no sale id, attempt id or failed-attempt counter |
sub-pause |
The subscription was paused |
sub-cancel |
The subscription was cancelled, by hand or automatically after the last failed attempt (status 5). Always contains seller_payme_id, seller_id, sub_payme_id, sub_payme_code; subscription_id only if you set it |
sub-complete |
The subscription completed |
The payload includes sub_payme_id, sub_status, sub_price, iteration counters and buyer fields (card mask, name, email, phone and ID; never the buyer_key). The iteration counters count successful charges only. Each successful iteration also creates a sale, which sends its own sale-complete or sale-failure. Handlers must be idempotent: re-query get-subscriptions before acting. Charge retries (once a day, up to 7 attempts) are described on Recurring Payments.
sub_callback_url is sent on every successful charge while the subscription is active and on every failed attempt that leads to a retry.
Subscription callback payload
| Field | Example | Description |
|---|---|---|
notify_type |
sub-iteration-success |
Notification type (table above) |
status_code |
0 |
0 success, 1 error |
status_error_code |
In case of an error, PayMe's error code | |
sub_error_text |
In case of an error, the error message | |
seller_payme_id |
MPLDEMO-MPLDEMO-MPLDEMO-1234567 |
PayMe's seller ID |
seller_id |
12345 |
Your seller ID |
sub_payme_id |
XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX |
PayMe's subscription ID |
sub_payme_code |
1234 |
PayMe's subscription code |
sub_created |
2016-01-01 15:15:15 |
Subscription creation time |
sub_payment_date |
2016-01-01 18:32:23 |
Subscription payment time |
sub_start_date |
2016-01-02 00:00:00 |
When the first iteration was charged |
sub_next_date |
2016-01-03 00:00:00 |
Next iteration time |
sub_status |
1 |
See Subscription Statuses |
sub_iteration_type |
1 |
See Subscription Iteration Types |
sub_currency |
USD |
ISO 4217 code |
sub_price |
10000 |
Price of one iteration in minor units (50.75 → 5075) |
sub_iterations |
4 |
Number of iterations |
sub_iterations_completed |
4 |
Completed iterations |
sub_iterations_left |
4 |
Remaining iterations |
buyer_card_mask |
458045******4580 |
Masked card number |
buyer_name, buyer_email, buyer_phone, buyer_social_id |
Buyer details |
Israeli direct debit
sale-paidwhen the debit is submitted. Take the token (buyer_key) fromsale-il-direct-debit-updateorget-sales, not fromsale-paid.- When the bank authorization status changes:
sale-completeorsale-failure, thensale-il-direct-debit-updatewithauthorization_statusandauthorization_error_code. Theauthorization_statusvalues are listed on Errors (section "Israeli direct debit").
An Israeli direct-debit payment sends sale-paid when submitted, then sale-complete about 8 days later, or sale-failure if the bank returns the debit before that.
Sellers (partners)
Sent to the partner's callback URL with all seller_* and fee_* fields: seller-create (adds seller_public_key), seller-update, seller-approve. Related partner notifications: withdrawal-complete, charge-complete, and point-of-sale-action-* (for example after a POS device is paired).
Seller callback payload
| Field | Example | Description |
|---|---|---|
status_code |
0 |
0 success, 1 error |
status_error_code |
In case of an error, PayMe's error code | |
status_error_details |
In case of an error, the error message | |
notify_type |
seller-create |
Notification type (above) |
seller_payme_id |
MPLDEMO-MPLDEMO-MPLDEMO-1234567 |
PayMe's seller ID |
seller_id |
12345 |
Your seller ID |
seller_created |
2016-01-01 15:15:15 |
Seller creation time |
seller_first_name |
First |
First name of the account owner |
seller_last_name |
Last |
Last name of the account owner |
seller_social_id |
999999999 |
Social ID of the account owner |
seller_birthdate |
1989-05-06 |
Birth date, yyyy-mm-dd (the request sends DD/MM/YYYY) |
seller_gender |
0 |
0 male, 1 female |
seller_email |
personal@example.com |
Seller's personal email |
seller_contact_email |
contact@example.com |
Contact email shown to buyers |
seller_phone |
0500000042 |
Seller's personal phone |
seller_contact_phone |
031234567 |
Contact phone shown to buyers |
seller_inc |
2 |
See Seller Incorporation Types (IL) |
seller_inc_code |
123456 |
Business ID (ח.פ, ע.מ) |
seller_merchant_name |
Baby Ducks |
Merchant name |
seller_site_url |
www.babyducks.com |
Site URL |
seller_address_city |
Tel Aviv |
Business address: city |
seller_address_street |
Rothschild |
Business address: street |
seller_address_street_number |
45 |
Business address: street number |
seller_address_country |
IL |
Business address: country (ISO 3166-1 alpha-2) |
seller_active |
1 |
0 inactive, 1 active |
seller_approved |
1 |
0 not approved, 1 approved |
seller_currencies |
[ILS, USD, EUR] |
Allowed currencies |
Bank transfer
A bank transfer reaching PAID is reported with a callback.
There is no bank-transfer-specific notify_type: the standard sale callbacks apply (sale-complete when the transfer is paid, sale-failure otherwise).
B2B payment contracts
Documented with the B2B Payments API: Contracts: getting started → Callbacks.
Verifying a callback (payme_signature)
How it is computed
Partners, who hold payme_client_key, verify callbacks with this formula; a seller without a partner key re-queries the sale instead (see below).
payme_signature is the lowercase hexadecimal MD5 hash of four values joined with no separator, in this order:
payme_client_key(the partner key)- the client password issued together with the client key: the partner's
merchant_password(calledpayme_merchant_secretin older PayMe documentation) payme_transaction_id(from the payload)payme_sale_id(from the payload)
It is a plain MD5 hash, not an HMAC.
The signature always uses your primary client key. If PayMe is moving you to a new client key, your API calls can already use the new key, but the signature stays on the primary key until PayMe completes the switch.
Worked example (fake values):
| Input | Value |
|---|---|
payme_client_key |
demo_partner_KEY12345 |
| client password | demoPassw0rd |
payme_transaction_id |
TRAN1700-000000AA-EXAMPLE1-TXN00001 |
payme_sale_id |
SALE1700-000000AA-EXAMPLE1-SALE0001 |
Joined string: demo_partner_KEY12345demoPassw0rdTRAN1700-000000AA-EXAMPLE1-TXN00001SALE1700-000000AA-EXAMPLE1-SALE0001
payme_signature = f8ffc5bb45fe6c3ad326c439f1a21c28
import hashlib, hmac
def payme_signature_ok(form, client_key, client_password):
expected = hashlib.md5(
(client_key + client_password
+ form["payme_transaction_id"] + form["payme_sale_id"]).encode()
).hexdigest()
return hmac.compare_digest(expected, form.get("payme_signature", ""))
The client password is issued together with your partner credentials by PayMe Partners (partners@payme.io). A merchant that has no payme_client_key cannot compute the signature: verify the sale instead with POST /get-sales or POST /get-transactions, which is also the check PayMe support recommends.
Where the same signature appears
The same formula is used in sale callbacks, the return-URL redirect (including when payme_status=error), and the capture-sale and refund-sale responses. In each case payme_transaction_id is the transaction in that payload — the payment, the capture or the refund.
Exceptions:
- Seller callbacks and
withdrawal-completehave no signature. Ignore the body and re-read withget-sellersorget-withdrawals. - Subscription callbacks have no signature. The subscription return URL is signed: the MD5 of the client key, the client password, the URL's
transaction_id(PayMe's transaction ID; this URL has nopayme_transaction_id) andsub_payme_id. verify_onlyrefunds returnpayme_signature: null.- B2B payment contracts use a shorter formula: the MD5 of the client key, the client password and the contract's
guid. See B2B Payments.
Confirm the sale before you fulfil
A valid signature tells you the callback refers to a real PayMe sale and transaction. Treat the status and amount in a callback as a notification, not as proof of payment.
Safe practice: before you fulfil an order, call POST /get-sales with sale_payme_id and check sale_status and sale_price against your order. POST /get-transactions works too, and shows each charge and refund as a row, which is why the AI Toolkit steps use it.
Find the order by the payme_sale_id you stored when you created the sale, and match it on your own order ID as well as the amount. Otherwise a real, cheap sale's ID could be replayed in a forged callback against an expensive order. If you confirm with get-transactions instead, send transaction_type: 40 for a sale that is authorized and not yet captured: the default query doesn't return it.
PayMe does not publish a fixed list of callback source addresses, so do not allow-list by IP. Authenticate callbacks by their signature and confirm the sale with get-sales.
Delivery, retries, duplicates and ordering
What counts as delivered
- Any status below 400 counts as delivered. The response body is ignored.
- Each attempt times out after 5 seconds. Reply
200immediately and do the slow work (emails, fulfilment) afterwards.
Retries
- PayMe makes up to 3 attempts: the first, a retry about 30 seconds later, and a last retry about 60 seconds after that.
- A 4xx or 5xx response, a timeout, or a connection error triggers a retry.
- After the last attempt the callback is dropped. It cannot be replayed through the API.
- If your host keeps returning 5xx, 408 or 429 responses, timing out or refusing connections, PayMe pauses deliveries to that host for a short time. Callbacks during the pause are not delivered or retried.
- A pause is triggered by 5xx responses,
429,408, timeouts and connection errors on your host; an ordinary 4xx (for example a signature or authentication rejection by your code) never causes one. It lifts by itself once your endpoint answers. You can't reset it yourself; if you need it cleared at once, contact PayMe support with the callback host. There is no test ping. Acknowledge first (cold starts included) and process in the background. - Order is not guaranteed: act on
notify_typeand the sale status, not on the order of arrival.
Do not rely on callbacks alone. Reconcile with POST /get-sales.
Duplicates
The same callback can arrive more than once — typically when your endpoint processes it but replies after the 5-second timeout. There is no event ID. De-duplicate on notify_type + payme_sale_id + payme_transaction_id; each refund, capture and failed attempt has its own payme_transaction_id.
Ordering
Callbacks are not guaranteed to arrive in order: they are sent by parallel workers with retry delays, so a refund can arrive before a sale-complete that is still being retried, and there is no sequence number or event timestamp. Never let a later-arriving callback overwrite a final state you already recorded, and query the sale when in doubt.
Handling checklist
- Parse the body as form data and reply
200within 5 seconds. - If you are a partner, verify
payme_signature(sale callbacks). - Ignore it if you already processed this
notify_type+payme_sale_id+payme_transaction_id. - Before fulfilling, confirm
sale_statusandsale_pricewithPOST /get-sales. - Run a periodic reconciliation with
get-sales, because callbacks can be dropped after 3 attempts.