Cancelling a payment
Cancel a payment when the order is gone — the payer left, the basket changed — and nobody paid yet.
#!/usr/bin/env bash# Cancel a payment nobody paid yet. The body is empty and signed as such.set -euo pipefail. "$(dirname "$0")/../signed-request.sh"
payment_id=$(mka_required PAYMENT_ID)mka_request POST "/v1/payments/$payment_id/cancel" "" "$(mka_new_key)"// Cancel a payment nobody paid yet. The body is empty and signed as such.import { newKey, print, request, required } from "../signed-request.mjs";
const paymentId = required("PAYMENT_ID");const answer = await request("POST", `/v1/payments/${paymentId}/cancel`, { idempotencyKey: newKey(),});print(answer);<?php// Cancel a payment nobody paid yet. The body is empty and signed as such.
declare(strict_types=1);
require __DIR__ . '/../signed_request.php';
$paymentId = mka_required('PAYMENT_ID');mka_show(mka_request( 'POST', "/v1/payments/{$paymentId}/cancel", null, mka_new_key(),));"""Cancel a payment nobody paid yet. The body is empty and signed as such."""from signed_request import new_key, request, required, show
payment_id = required("PAYMENT_ID")show(request( "POST", f"/v1/payments/{payment_id}/cancel", idempotency_key=new_key(),))//go:build ignore
// Cancel a payment nobody paid yet. The body is empty and signed as such.package main
import ( "docs.processing.yolotech.cc/examples/signedrequest")
func main() { paymentID := signedrequest.Required("PAYMENT_ID") signedrequest.Show(signedrequest.Request("POST", "/v1/payments/"+paymentID+"/cancel", nil, signedrequest.NewKey()))}The body is empty and signed as such. The answer is 202 Accepted, not “cancelled”: the account may already
be in the payer’s bank app. The payment carries cancellation {id, status}:
cancellation.status |
Meaning |
|---|---|
requested, processing |
The provider is withdrawing the account. The payment is processing. |
succeeded |
Cancelled. The payment is cancelled, and a webhook says so. |
rejected |
Too late: the money came first. The payment goes on to succeeded; refund it if you must. |
Read the payment, or wait for its webhook, to learn the outcome.
Refusals
Section titled “Refusals”| Answer | Why | Do |
|---|---|---|
409 payment_busy + Retry-After: 1 |
The account is being assigned right now. | Retry after a second with the same Idempotency-Key. |
409 payment_not_cancellable |
Paid, ended, or being checked. | Read the payment. Refund a succeeded one. |
404 payment_not_found |
Not your payment. | Check the id. |
The same key again answers the cancellation’s current state, never a second cancellation.
Correct the amount instead
Section titled “Correct the amount instead”While the payer has not paid and the window is open, change the amount on the same account rather than cancel and create:
#!/usr/bin/env bash# Change the amount the payer is to transfer while the window is open.set -euo pipefail. "$(dirname "$0")/../signed-request.sh"
payment_id=$(mka_required PAYMENT_ID)body=$(cat <<JSON{ "amount": "${AMOUNT:-2600.00}", "currency": "LKR", "payment_version": $(mka_required PAYMENT_VERSION)}JSON)mka_request POST "/v1/payments/$payment_id/amount-correction" "$body" "$(mka_new_key)"// Change the amount the payer is to transfer while the window is open.import { newKey, print, request, required } from "../signed-request.mjs";
const paymentId = required("PAYMENT_ID");const answer = await request("POST", `/v1/payments/${paymentId}/amount-correction`, { body: { amount: process.env.AMOUNT ?? "2600.00", currency: "LKR", payment_version: Number(required("PAYMENT_VERSION")), }, idempotencyKey: newKey(),});print(answer);<?php// Change the amount the payer is to transfer while the window is open.
declare(strict_types=1);
require __DIR__ . '/../signed_request.php';
$paymentId = mka_required('PAYMENT_ID');mka_show(mka_request( 'POST', "/v1/payments/{$paymentId}/amount-correction", [ 'amount' => getenv('AMOUNT') ?: '2600.00', 'currency' => 'LKR', 'payment_version' => (int) mka_required('PAYMENT_VERSION'), ], mka_new_key(),));"""Change the amount the payer is to transfer while the window is open."""import os
from signed_request import new_key, request, required, show
payment_id = required("PAYMENT_ID")show(request( "POST", f"/v1/payments/{payment_id}/amount-correction", body={ "amount": os.environ.get("AMOUNT", "2600.00"), "currency": "LKR", "payment_version": int(required("PAYMENT_VERSION")), }, idempotency_key=new_key(),))//go:build ignore
// Change the amount the payer is to transfer while the window is open.package main
import ( "strconv"
"docs.processing.yolotech.cc/examples/signedrequest")
func main() { paymentID := signedrequest.Required("PAYMENT_ID") version, err := strconv.Atoi(signedrequest.Required("PAYMENT_VERSION")) if err != nil { panic(err) } signedrequest.Show(signedrequest.Request("POST", "/v1/payments/"+paymentID+"/amount-correction", map[string]any{ "amount": signedrequest.Env("AMOUNT", "2600.00"), "currency": "LKR", "payment_version": version, }, signedrequest.NewKey()))}Send the payment_version you saw. The answer is 202 with correction {id, status}; once applied, the
payment moves to the next version with the new amount. A stale version is 409 payment_version_conflict, a
payment no longer waiting for money is 409 payment_not_correctable, the same amount is
409 amount_unchanged.