You can cancel authorized payments and refund customers with the TossPaymetns API. Learn how to make partial cancellations, cancel virtual account payments, and safely cancel payments.
To cancel an authorized payment, input the paymentKey
that you received from the payment approval API and the cancelReason
that outlines the reason cancellation. Add paymentKey
to as a path parameter and cancelReason
to the request body.
curl --request POST \
--url https://api.tosspayments.com/v1/payments/dxRrMScPUSvrdmfYps_UG/cancel \
--header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \
--header 'Content-Type: application/json' \
--data '{"cancelReason":"Customer wants refund"}'
As a response, the cancellation object is returned in the cancels
field as an array.
{
"mId": "tosspayments",
"version": "2022-11-16",
"lastTransactionKey": "U2b2cx9DfHNEodrT4vb88",
"paymentKey": "dxRrMScPUSvrdmfYps_UG",
"orderId": "sd7bqJRAFeNxm91hKfqGO",
"orderName": "Toss T-Shirts",
"currency": "KRW",
"method": "CARD",
"status": "CANCELED",
"requestedAt": "2022-01-01T11:31:29+09:00",
"approvedAt": "2022-01-01T11:31:51+09:00",
"useEscrow": false,
"cultureExpense": false,
"checkout": {
"url": "https://api.tosspayments.com/v1/payments/dxRrMScPUSvrdmfYps_UG/checkout"
},
"card": {
"issuerCode": "33",
"acquirerCode": "31",
"number": "12341234****123*",
"installmentPlanMonths": 0,
"isInterestFree": false,
"interestPayer": null,
"approveNo": "00000000",
"useCardPoint": false,
"cardType": "CREDIT",
"ownerType": "PERSONAL",
"acquireStatus": "READY"
},
"virtualAccount": null,
"transfer": null,
"mobilePhone": null,
"giftCertificate": null,
"foreignEasyPay": null,
"cashReceipt": null,
"cashReceipts": null,
"discount": null,
"cancels": [
{
"cancelReason": "Customer wants refund",
"canceledAt": "2022-01-01T11:32:04+09:00",
"cancelAmount": 10000,
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 0,
"easyPayDiscountAmount": 0,
"transactionKey": "8B4F646A829571D870A3011A4E13D640",
"receiptKey": "V4AJ6AhSWsGN0RocizZQlagPLN8s2IahJLXpfSHzQBTKoDG7"
}
],
"secret": null,
"type": "NORMAL",
"easyPay": "TOSSPAY",
"country": "KR",
"failure": null,
"totalAmount": 10000,
"balanceAmount": 0,
"suppliedAmount": 0,
"vat": 0,
"taxFreeAmount": 0,
"taxExemptionAmount": 0
}
Add an IDEMPOTENCY_KEY
to the request header to avoid cancelling the same payment twice.
To cancel a payment partially, insert the amount you would like to cancel in cancelAmount
when making a request to the payment cancellation API.
curl --request POST \
--url https://api.tosspayments.com/v1/payments/gGsAxGdRW63h5BdOmboSu/cancel \
--header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \
--header 'Content-Type: application/json' \
--data '{"cancelReason":"Customer wants refund","cancelAmount":1000}'
When the payment has more than one partial cancellation, several cancellation objects will appear in the cancels field as below.
// Payment object
{
// ...
"cancels": [
{
"cancelAmount": 1000,
"cancelReason": "Customer wants refund",
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 9000,
"easyPayDiscountAmount": 0,
"canceledAt": "2022-01-01T23:23:52+09:00",
"transactionKey": "8B4F646A829571D870A3011A4E13D640",
"receiptKey": "CuskOnzZEf0Xbwo8eMZ56slTAXbJ8jUjTX3n6SNuvY5d7Fpf"
},
{
"cancelAmount": 1000,
"cancelReason": "Customer wants to cancel other items as well",
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 8000,
"easyPayDiscountAmount": 0,
"canceledAt": "2022-01-02T20:00:00+09:00",
"transactionKey": "6673C15BF350C3F9BF45CEFC48C7A24E",
"receiptKey": "PLDm1CZSQxTBvYrHytz3yt3MU09Nx57IoCxIEJ8HPzOyIRos"
}
]
// ...
}
// Payment 객체
{
// ...
"cancels": [
{
"cancelAmount": 1000,
"cancelReason": "고객이 취소를 원함",
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 9000,
"easyPayDiscountAmount": 0,
"canceledAt": "2022-01-01T23:23:52+09:00",
"transactionKey": "8B4F646A829571D870A3011A4E13D640",
"receiptKey": "CuskOnzZEf0Xbwo8eMZ56slTAXbJ8jUjTX3n6SNuvY5d7Fpf"
},
{
"cancelAmount": 1000,
"cancelReason": "고객이 다른 품목도 취소를 원함",
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 8000,
"easyPayDiscountAmount": 0,
"canceledAt": "2022-01-02T20:00:00+09:00",
"transactionKey": "6673C15BF350C3F9BF45CEFC48C7A24E",
"receiptKey": "PLDm1CZSQxTBvYrHytz3yt3MU09Nx57IoCxIEJ8HPzOyIRos"
}
]
// ...
}
To cancel virtual account payments, include the customer's refund account information in refundReceiveAccount
.
Like other cancellation requests, the response is returned in the cancels
field of the Payment object.
curl --request POST \
--url https://api.tosspayments.com/v1/payments/{paymentKey}/cancel \
--header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \
--header 'Content-Type: application/json' \
--data '{"cancelReason":"Customer wants refund"}'
POST /v1/payments/{paymentKey}/cancel
Cancel authorized payments with paymentKey.
* Add an IDEMPOTENCY_KEY
to the request header to avoid cancelling the same payment twice.
- paymentKey 필수 · string
The payment key. The maximum length is 200 characters.
- cancelReason 필수 · string
The reason for canceling payments. The maximum length is 200 characters.
- cancelAmount 필수 · number
The amount to be canceled. The default value is 0.
- refundReceiveAccount 필수 · obejct
The information of customer's refund account information. This parameter is required only for virtual account payments. You don’t need this object when canceling payments with other payment methods.
- bank 필수 · string
The bank code of customers’ accounts for refunds. Please refer to the bank codes.
- accountNumber 필수 · string
The account number to receive refunds. Only numbers should be entered. (
-
is not allowed.) The maximum length is 20 characters. - holderName 필수 · string
The name of account holder. The maximum length is 60 characters.
- taxFreeAmount 필수 · number
The tax-exempted amount out of the amount to be canceled. The default value is 0.
* This parameter will work only for duty-free stores or stores selling both tax-free and taxable products. Stores selling taxable products cannot use this function. For more information, please refer to how to take care of taxes.
- refundableAmount number
This is for the available amount for refunds. Use this parameter to make sure you don't cancel more than the actual refundable amount.
If the available amount for refunds is different from refundableAmount, an error is sent without processing the cancellation.
* This parameter is no longer recommended. Use
IDEMPOTENCY_KEY
in request header to cancel payments safely.
If the payment cancellation is successful, the cancellation object is returned in the cancels
field as an array.
{
"mId": "tosspayments",
"version": "2022-11-16",
"paymentKey": "2WkABYDxNyJQbgMGZzorzYWwKBG9kVl5E1em4dKva7XL9njP",
"lastTransactionKey": "66630503F0C4BA6F33FF33EAC15249C2",
"orderId": "O-1644460169123",
"orderName": "토스 티셔츠 외 2건",
"method": "간편결제",
"status": "CANCELED",
"requestedAt": "2022-01-01T11:31:29+09:00",
"approvedAt": "2022-01-01T11:31:51+09:00",
"useEscrow": false,
"cultureExpense": false,
"checkout": {
"url": "https://api.tosspayments.com/v1/payments/2WkABYDxNyJQbgMGZzorzYWwKBG9kVl5E1em4dKva7XL9njP/checkout"
},
"card": {
"amount": "1000",
"issuerCode": "33",
"acquirerCode": "31",
"number": "12341234****123*",
"installmentPlanMonths": 0,
"isInterestFree": false,
"interestPayer": null,
"approveNo": "00000000",
"useCardPoint": false,
"cardType": "CREDIT",
"ownerType": "PERSONAL",
"acquireStatus": "READY"
},
"virtualAccount": null,
"transfer": null,
"mobilePhone": null,
"giftCertificate": null,
"foreignEasyPay": null,
"cashReceipt": null,
"cashReceipts": null,
"receipt": {
"url": "https://dashboard.tosspayments.com/sales-slip?transactionId=bGqvzDSq5OoimabqhwIGRNk5Ks4A%2B2pzVwKxOP0WsjnZ6FZiUqVa4RbnVeqVlxsd&ref=PX"
},
"discount": null,
"cancels": [
{
"cancelReason": "Customer wants refund",
"canceledAt": "2022-01-01T11:32:04+09:00",
"cancelAmount": 1000,
"taxFreeAmount": 0,
"taxExemptionAmount": 0,
"refundableAmount": 0,
"easyPayDiscountAmount": 0,
"transactionKey": "66630503F0C4BA6F33FF33EAC15249C2",
"receiptKey": "Ik43Gc0hQldGN0l8iPRIbA0YMNdMRnPcZGIQnpLkzU6mhvE0"
}
],
"secret": null,
"type": "NORMAL",
"easyPay": "TOSSPAY",
"country": "KR",
"failure": null,
"currency": "KRW",
"totalAmount": 1000,
"balanceAmount": 0,
"suppliedAmount": 0,
"vat": 0,
"taxFreeAmount": 0,
"taxExemptionAmount": 0
}
If payment cancellation fails, the error object is returned with the HTTP status code like below
{
"code": "NOT_FOUND_PAYMENT",
"message": "Not found payment"
}
code
: Error code that shows the error type.message
: Error message that shows the reason for the error.
Find out errors that can possibly occur in the payment cancellation API.
With the keyed transaction API, customers can manually enter their card numbers to make payments. The keyed transaction, also called a “hand-keyed transaction” or “key-in transaction”, is a payment method without authorization that customers manually key in their credit card numbers. An authorized payment refers to a payment method with the Internet Secure Payment (ISP) process provided by card companies.
The keyed transaction API is available after you sign an additional contract. Please contact TossPayments to use this API.
The request body of the keyed transaction API should include the information about orders and card numbers. Card numbers and their expiration dates are required parameters. When signing a contract with TossPayments, you can choose to include field options like passwords and card holders’ date of birth as required parameters. The card transaction authorization response needs to include the card field.
* The keyed transaction API is available after signing an additional contract.
POST /v1/payments/key-in
Request payments with card numbers and additional card information.
curl --request POST \
--url https://api.tosspayments.com/v1/payments/key-in \
--header 'Authorization: Basic dGVzdF9za196WExrS0V5cE5BcldtbzUwblgzbG1lYXhZRzVSOg==' \
--header 'Content-Type: application/json' \
--data '{"amount":15000,"orderId":"a4CWyWY5m89PNh7xJwhk1","orderName":"Toss T-Shirts","customerName":"Tom Kim","cardNumber":"4330123412341234","cardExpirationYear":"24","cardExpirationMonth":"07","cardPassword":"12","customerIdentityNumber":"881212"}'
- amount 필수 · integer
The amount to be paid.
- orderId 필수 · string
The order ID. Generate a random string between 6 and 64 characters, using upper and lowercase letters, numerals, -, and _. (No other special characters are allowed.) - cardNumber 필수 · string
The card number. The maximum length is 20 characters. - cardExpirationYear 필수 · string
The card expiration year. - cardExpirationMonth 필수 · string
The card expiration month. - cardPassword string
The first two digits of the card password (PIN). - orderName 필수 · string
The name of an order. E.g., bottled water and 1 other item. The maximum length is 100 characters. - customerIdentityNumber 필수 · string
The card holder information. This string has 6 digits of the card holder’s date of birth (YYMMDD) or 10 digits of a business registration number. - cardInstallmentPlan integer
The monthly installment period. You can configure the monthly installment period manually. You can enter values between 2 and 12. Entering 0 will trigger a one-time payment, not an installment. - useFreeInstallmentPlan boolean
Whether or not to apply an interest-free installment plan supported by the card company. The default value is false. If the value is true, an interest-free installment plan will be applied for the months set at
cardInstallmentPlan
.* The interest-free installment plan fees incurred by this parameter is paid by the store.
- taxFreeAmount integer
This is the tax-exempted amount out of the amount to be canceled. The default value is 0 for any missing parameters.
* This parameter will work only for duty-free stores or stores selling both tax-free and taxable products. Stores selling taxable products cannot use this function. For more information, please refer to how to take care of taxes.
- customerEmail string
The customer’s email address. Customers will be alerted about their payment result through this email. The maximum length is 100 characters. - customerName string
The customer’s name. The maximum length is 100 characters. - vbv string
This is used for the 3D Secure(3DS) authentication when customers are making card payments overseas. It is a required object if you have to send the 3DS authentication result. - cavv string
The authentication value for the 3DS authentication session - xid string
The transaction ID. - eci string
The code value for the 3DS authentication result.
If the keyed transaction is successful, the Payment object is returned.
If keyed transactions fail, the error object is returned with the HTTP status code like below.
{
"code": "NOT_FOUND_PAYMENT",
"message": "Not found payment"
}
code
: Error code that shows the error type.message
: Error message that shows the reason for the error.
See the errors that can occur in the keyed transaction API.
With webhooks, you can receive real-time notifications when a payment is updated. Integrate webhooks and get real-time updates for customers’ payment methods, executions of payouts, and customers’ payment methods for the BrandPay service.
Below are suported webhooks events. Select an event type to learn more.
Type of Event | Description |
---|---|
PAYMENT_STATUS_CHANGED | Change in the payment status, applicable to all payment methods. |
PAYOUT_STATUS_CHANGED | Successful or failed executions of sub-mall payouts. |
METHOD_UPDATED | Change in customers’ payment methods in the BrandPay service. |
CUSTOMER_STATUS_CHANGED | Change in customers’ status in the BrandPay service. |
DEPOSIT_CALLBACK | Depositing money or canceling deposits for virtual account payments. |
Click on “Register Webhooks” on the webhook page of the TossPayments Developer Center. In the popup window, enter the name and URL of the webhook and select the event you want. Click on “Register” and the webhook iss registered.
Check out the webhook list to confirm whether the webhook is properly registered. You can delete registered webhooks in the detail page.
Once the registered event occurs, you will receive the webhook event at the registered URL.
The event is a JSON file sent through HTTP POST method. Check whether your server can handle a JSON file. HTTP is supported, but HTTPS is recommended as it is more secure.
Webhook URLs must be accessible online. Since a local development environment is not publicly accessible, a local server port cannot be registered. Or you can test webhooks with tools that help you access a local development environment. ngrok can help you publicly access your local server in a secure manner. Create an URL that can access your local server port and test webhooks.
a. Download ngrok
b. Add your local server’s port number such as ngrok http 8080 to a command line in your terminal and execute the command.
c. Add a webhook endpoint to a server-side code and design a logic you want.
d. After running ngrok, register a forwarding URL to localhost:8080 on the Webhook page in TossPayments
e. Developer Center. You must add the webhook endpoint created during Step 3 at the end of the URL created via ngrok and register.
f. Run a test payment. A webhook will be sent to the registered URL. You can see the incoming event data in the ngrok inspector (Web Inspection Interface) as below.
In the webhook page of TossPayments Developer Center, click on each webhook to see the webhook records. Webhook records are the status of the registered events. So, each record shows the change of sending status when each event is triggered. The webhook logs sent from events are not provided.
The status of each event is as follows.
When the webhook is in “Sending” status, clicking on “Retry” nullifies the original request, and starts the new request as the first attempt.
When the HTTP 2xx code returns as a response after a webhook is sent, it indicates a success; otherwise, a failure. If the first attempt to send a webhook fails, the system will resend the webhook up to 7 times (up to 3 days and 19 hours after the first attempt). Time intervals increase gradually by attempt (1 minute → 4 minutes → 16 minutes → 64 minutes → 256 minutes → 1024 minutes → 4096 minutes).
Number of Re-sending Attempt(s) | Time Intervals (minutes) |
---|---|
1 | 1 |
2 | 4 |
3 | 16 |
4 | 64 |
5 | 256 |
6 | 1024 |
7 | 4096 |
If sending a webhook keeps failing, please check the firewall configuration whether the port number for the registered webhook URL is allowed. If the last attempt fails, you will be notified with the information of the failed event. Check your email and see whether you have issues with your server or the webhook URLs.
Webhooks that notify you of card, bank transfer, cell phone, and gift certificate payment statuses. Take a look at the payment statuses that the webhooks are sent to in the figure below.
If the customer does not authorize in the checkout window within the validity time (30 minutes), or if the store does not call the payment authorization API after the customer authorizes the payment, then the payment status changes to EXPIRED.
If the customer closes the checkout window, then the webhook is also not sent because the payment status doesn't change.
Billingdoesn't send a webhook when the payment is completed because the payment request is followed by authorization.
{
"eventType": "PAYMENT_STATUS_CHANGED",
"createdAt": "2022-01-01T00:00:00.000000",
"data": {
"mId": "tosspayments",
"version": "2022-11-16",
"lastTransactionKey": "B7103F204998813B889C77C043D09502",
"paymentKey": "",
"orderId": "",
"status": "DONE",
"requestedAt": "2022-08-05T12:56:00+09:00",
"approvedAt": "2022-08-05T12:56:21+09:00",
"useEscrow": false,
"card": {
"issuerCode": "61",
"acquirerCode": "31",
"number": "48902300****406*",
"installmentPlanMonths": 0,
"amount": 10000,
//...
},
//...
}
}
- eventType 필수 · number
The webhook event type.
- createdAt 필수 · string
The time the webhook was created. Use the ISO 8601 format
yyyy-MM-dd'T'HH:mm:ss.SSSSSS
. - data 필수 · object
The Payment object whose state changed.
This is a webhook that notifies you of the status of a virtual account payment. The payment status that the webhook is sent to is shown in the figure below.
If a deposit error has been occurred due to the customer's account transfer limit has been exceeded, or a network issue, then the payment status will be changed from DONE
to WAITING_FOR_DEPOSIT
. You need to instruct the paying customer to deposit again.
Until version 1.4, the payment status changes from DONE
to CANCELED
when a deposit error occurs. Similarly, you should instruct the paying customer to re-deposit.
{
"createdAt": "2022-01-01T00:00:00.000000",
"secret": "",
"status": "DONE",
"transactionKey": "9FF15E1A29D0E77C218F57262BFA4986",
"orderId": ""
}
- createdAt 필수 · string
The time the webhook was created. Use the ISO 8601 format
yyyy-MM-dd'T'HH:mm:ss.SSSSSS
. - secret 필수 · string
A value to validate that the virtual account webhook request is legitimate. If it is equal to
secret
returned in the response from the Payment authorization API, the request is legitimate. - status 필수 · string
Payment status.
- transactionKey 필수 · string
A key that identifies the virtual account transaction whose status has changed.
- orderId 필수 · string
Order ID.
If the virtual account status changes when you register both the [PAYMENT_STATUS_CHANGED
] (#payment_status_changed) and [DEPOSIT_CALLBACK
] (#deposit_callback) events, the webhook will also be sent twice.
Webhook that sent when the status of a payout request changes to COMPLETED
, FAILED
. For detailed status descriptions, see status
.
{
"eventType": "PAYOUT_STATUS_CHANGED",
"createdAt": "2022-01-01T00:00:00.000000",
"data": {
"payoutKey": "",
"subMallId": "testmall100",
"payoutDate": "20220103",
"payoutAmount": 10000,
"requestedAt": "20220101100130",
"account": {
"bankCode": "03",
"accountNumber": "00123412341234",
"holderName": null
},
"status": "COMPLETED",
"metadata": null,
"failure": null,
"transferSummary": null
}
}
- eventType 필수 · number
The webhook event type.
- createdAt 필수 · string
The time the webhook was created. Use the ISO 8601 format
yyyy-MM-dd'T'HH:mm:ss.SSSSSS
. - data 필수 · object
The [Payout object] (/reference#payout-object) that state has changed.
A webhook event is sent when a BrandPay customer's payment method changes.
{
"eventType": "METHOD_UPDATED",
"createdAt": "2022-05-12T00:00:00.000000",
"data": {
"customerKey": "",
"methodKey": "",
"status": "ENABLE"
}
}
- eventType 필수 · number
The webhook event type.
- createdAt 필수 · string
The time the webhook was created. Use the ISO 8601 format
yyyy-MM-dd'T'HH:mm:ss.SSSSSS
. - data 필수 · object
The following three fields are returned.
customerKey
: The unique ID of the customer created in your store.methodKey
: The key that specifies the payment method.status
: The status of the payment method.- `ENABLED: The payment method is registered and available for use.
DISABLED
: The payment method is deleted and unavailable.- `ALIAS_UPDATED: The alias of the registered payment method has been changed.
Webhook that sent when a BrandPay customer's status changes.
{
"eventType": "CUSTOMER_STATUS_CHANGED",
"createdAt": "2022-01-01T00:00:00.000000",
"data": {
"customerKey": "",
"status": "PASSWORD_CHANGED",
"changedAt": "2022-01-01T00:00:00+09:00"
}
}
- eventType 필수 · number
The webhook event type.
- createdAt 필수 · string
The time the webhook was created. Use the ISO 8601 format
yyyy-MM-dd'T'HH:mm:ss.SSSSSS
. - data 필수 · object
The following three fields are returned.
- customerKey`: The unique ID of the customer created in your store.
- status: The status of the customer's use of BrandPay.
CREATED
: The status of the customer's subscription to the checkout.REMOVED
: Unsubscribed from easy checkout.PASSWORD_CHANGED
: Password changed.ONE_TOUCH_ACTIVATED
: Activated one-touch payment.ONE_TOUCH_DEACTIVATED
: Deactivated one-touch payment.
changedAt
: When the change occurred. Use the ISO 8601 formatyyyy-MM-dd'T'HH:mm:ss±hh:mm
.