Run a B2B payment contract end to end

Prerequisites

A payment contract moves money between two PayMe sellers on a date you set, after the other seller approves it. This guide takes one contract from start to finish: choose the direction, prepare the payment method, create the contract, follow its approval with callbacks, change or cancel it, attach invoices, and handle errors. The concepts are on B2B payment contracts, and each step links to the reference page with the full field tables.

Every call in this guide:

Keep the MPL on your server. See Hosts and authentication.

Step 1: Choose the direction and the payment method

The seller that creates the contract is the initiator. The other seller is the recipient, and the recipient always approves the contract. payment.type sets who pays:

payment.type Use it to Who pays Who approves
1 – send Pay another seller, for example a supplier The initiator The recipient
2 – request Collect a payment from another seller The recipient The recipient

payment.method sets where the money comes from:

payment.method Paid from Limits
funds-transfer The payer's PayMe balance Don't send buyer_key (error 394)
credit-card A card attached to the payer's seller account Up to 5000000 (50,000.00) per contract (error 393)
il-direct-debit An Israeli direct-debit authorization attached to the payer's seller account ILS only (error 320). Up to 5000000 per contract (error 393)

The currency must be enabled for both sellers (error 252).

The payment requests in this guide use funds-transfer. A request paid by card or direct debit depends on a payment method that the recipient sets up for your contracts in PayMe: contact PayMe before you build one.

Step 2: Prepare the payer's card or direct debit

Do this step only for a contract that sends a payment (payment.type 1) by credit-card or il-direct-debit. With funds-transfer, go to step 3.

A card:

  1. Tokenize the card with generate-sale and sale_type token. The sale-complete callback carries the card's buyer_key.
  2. Attach the buyer_key to the initiator's seller account with add-card-to-seller.
  3. Check that the card appears in seller_credit_cards of get-sellers. It must still be valid on the contract's payment.due_date (error 390).

A direct-debit authorization: tokenize the bank account with "sale_payment_method": "il-direct-debit". You don't attach it yourself: once the bank approves it, PayMe attaches it to the seller account, and it appears in seller_bank_authorizations of get-sellers with auth_status Active. Until then it can't pay a contract (error 391).

The requests, with examples, are on Add a card or direct-debit authorization.

Step 3: Create the contract

Call POST /payment-contracts. This contract sends 5,000.00 by card on December 22:

curl -X POST https://sandbox.payme.io/api/payment-contracts \
  -H 'Content-Type: application/json' \
  -H 'PayMe-Merchant-Key: MPLDEMO-MPLDEMO-MPLDEMO-1234567' \
  -d '{
    "description": "December payment",
    "amount": 500000,
    "currency": "ILS",
    "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-1234567",
    "remote_id": "INV-2026-12",
    "callback_url": "https://your-server.example.com/payme/contracts",
    "recipient": {
      "email": "supplier@example.com",
      "phone": "0500000001",
      "name": "Example Supplies Ltd",
      "language": "en"
    },
    "payment": {
      "type": 1,
      "method": "credit-card",
      "due_date": "2026-12-22 10:00:00"
    }
  }'

The call returns HTTP 201 and the contract in status 1 (abridged):

{
  "status_code": 0,
  "guid": "CONT1700-000000AA-EXAMPLE1-CONT0001",
  "status": 1,
  "amount": 500000,
  "currency": "ILS",
  "remote_id": "INV-2026-12",
  "error_code": null
}

Store guid with your order: every later call uses it.

Send buyer_key at the top level of the body, as above. A buyer_key inside payment is ignored, and PayMe then picks the card itself: among the attached cards that are valid on payment.due_date, the one that expires last. For il-direct-debit, PayMe picks the last Active authorization. See Choose the payment method of a contract.

Name the recipient.

Set the due date in the future, as YYYY-MM-DD HH:MM:SS. This guide follows a one-time contract. To repeat the payment, add payment.recurring: see Create a payment contract.

Retrying a create is safe for 5 minutes. A request that repeats the initiator, recipient, amount, currency, payment.due_date and payment.type of a contract created in the last 5 minutes, and not failed, returns that contract with the same guid. It doesn't create a second contract or send a second payment-contract-created callback. After 5 minutes, the same request creates a new contract.

When the recipient has no PayMe account yet, PayMe can't tell two such recipients apart. A second contract to another new recipient, with the same amount, currency, due date and type within 5 minutes, returns the first contract. Compare the recipient in the response with the one you sent.

To request a payment instead, send payment.type 2. For example, to collect 1,200.00 from another seller's PayMe balance:

{
  "description": "Invoice 1002",
  "amount": 120000,
  "currency": "ILS",
  "remote_id": "INV-1002",
  "callback_url": "https://your-server.example.com/payme/contracts",
  "recipient": {
    "payme_id": "MPLDEMO-MPLDEMO-MPLDEMO-7654321"
  },
  "payment": {
    "type": 2,
    "method": "funds-transfer",
    "due_date": "2026-12-31 10:00:00"
  }
}

Step 4: Follow the approval and the payment

After you create the contract, PayMe asks the recipient to approve it, and the recipient approves in PayMe. Each change of status sends a callback to the contract's callback_url:

What happens status error_code notify_type
You created the contract 1 payment-contract-created
The recipient approves 2, then 3 payment-contract-recipient-approved, then payment-contract-initiator-approved
The recipient rejects 5 11000 payment-contract-recipient-rejected
Nobody approved in time 5 11002 or 11003 payment-contract-confirmation-timeout or payment-contract-payment-failed
You cancel the contract (step 6) 5 11001 payment-contract-canceled
The due date passed and the payment ran 4 payment-contract-payment-completed
The payment failed 5 11004 payment-contract-payment-failed

Handle the callbacks

Each callback is a POST with a form-encoded body (application/x-www-form-urlencoded, not JSON). It holds notify_type and every field of the contract, the same fields as the create response.

Read a contract

Read one contract with GET /payment-contracts/{guid}. Either seller on the contract can call it:

curl https://sandbox.payme.io/api/payment-contracts/CONT1700-000000AA-EXAMPLE1-CONT0001 \
  -H 'PayMe-Merchant-Key: MPLDEMO-MPLDEMO-MPLDEMO-1234567'

It returns the contract fields at the top level and again under data, plus status_code. A seller that isn't on the contract gets error 380.

To reconcile many contracts, page through List payment contracts, filtered by status, remote_id or the other filters on that page.

Step 5: Change a contract

Call PUT /payment-contracts/{guid} with the fields to change. Either seller on the contract can call it; another seller gets error 385. Change a contract before its due date.

You can change:

For example, to pay with another attached card:

curl -X PUT https://sandbox.payme.io/api/payment-contracts/CONT1700-000000AA-EXAMPLE1-CONT0001 \
  -H 'Content-Type: application/json' \
  -H 'PayMe-Merchant-Key: MPLDEMO-MPLDEMO-MPLDEMO-1234567' \
  -d '{
    "buyer_key": "BUYERDEMO-BUYERDEMO-BUYERDEMO-7654321",
    "payment": {
      "method": "credit-card"
    }
  }'

The response is the updated contract, with status_code 0.

You can't change amount, currency, payment.type, payment.due_date, payment.recurring, remote_id, callback_url, or the recipient's payme_id, business_code, email, phone or language: the call fails with HTTP 422 and error 20. To change any of them, cancel the contract and create a new one.

Step 6: Cancel a contract

Only the initiator can cancel. Call PUT /payment-contracts/{guid}/cancellations with a JSON body. error_note is your reason, and PayMe stores it on the contract:

curl -X PUT https://sandbox.payme.io/api/payment-contracts/CONT1700-000000AA-EXAMPLE1-CONT0001/cancellations \
  -H 'Content-Type: application/json' \
  -H 'PayMe-Merchant-Key: MPLDEMO-MPLDEMO-MPLDEMO-1234567' \
  -d '{"error_note": "Order cancelled"}'

Step 7: Attach invoices and other files

Either seller on the contract can attach files to it, such as an invoice or a receipt. Send the content Base64-encoded with Upload contract files:

curl -X POST https://sandbox.payme.io/api/payment-contracts/CONT1700-000000AA-EXAMPLE1-CONT0001/invoices \
  -H 'Content-Type: application/json' \
  -H 'PayMe-Merchant-Key: MPLDEMO-MPLDEMO-MPLDEMO-1234567' \
  -d '{
    "invoices": [
      {
        "name": "invoice-1001.pdf",
        "type": "Invoice Receipt",
        "base64": "JVBERi0xLjQK",
        "mime_type": "application/pdf"
      }
    ]
  }'

The response lists the files under Invoices (capital I), each with its id (INV…) and a url. Store the id.

Errors and what to do

Contract calls return status_code 1, with the code in status_error_code and the message in status_error_details. Check status_code, not only the HTTP status. These are the contract codes:

Code When What to do
380 No contract with this guid involves your seller account. Also returned when a seller that isn't the initiator tries to cancel Check the guid and the MPL in PayMe-Merchant-Key
381 Only recipient.business_code was sent and it matches no PayMe seller Add the recipient's email or phone
382 The action isn't allowed in the contract's current status, for example cancelling a completed contract Read the contract and act on its status
383 The recipient is the initiator's own account Name another seller
384 The recipient's seller account is inactive The recipient contacts PayMe support
385 Your seller account isn't a side of this contract, or didn't upload this file Call with the MPL of a seller on the contract, or of the file's uploader
386 Two files of the same type, or an unknown type, in one upload Send one file of each type per request
387 Two files with the same name in one upload Rename one of them
388 A replacement file has a different type Keep the file's type, or upload a new file
389 The payer has no card or direct-debit authorization of this method attached Do step 2 first
390 No attached card is valid on payment.due_date Attach a card that is valid on the due date, or move the date in a new contract
391 No direct-debit authorization is Active Wait for the bank's approval, or use another method
392 buyer_key isn't one of the payer's attached payment methods Take the buyer_key from get-sellers for the payer
393 A credit-card or il-direct-debit contract over 5000000 Lower the amount, or use funds-transfer
394 buyer_key sent with funds-transfer Remove buyer_key

Other codes you may meet: 1 (no seller key), 251 (unknown seller key), 163 (inactive seller), 252 (currency not enabled for one of the sellers), 25 (invalid recipient.phone), 20 (validation, HTTP 422; status_additional_info lists every message) and 900/901 (the body isn't valid JSON). The full tables are on Create a payment contract and B2B payment contracts.

Before you go live