South Korea P2P Gate
Accept bank-transfer pay-ins on a hosted page and send bank-account payouts in South Korean Won (KRW).
When to use it
The South Korea P2P Gate accepts bank-transfer pay-ins on a MagicPayments-hosted page —
the payer is shown the account details to transfer to — and sends bank-account payouts.
Amounts are in KRW (South Korean Won), which has zero decimal places:
50000 means ₩50,000.
| Operation | Rail | How the integration looks |
|---|---|---|
| Pay-in | Bank transfer (P2P) | Create an invoice, redirect the payer to the hosted page. |
| Pay-out | Bank account | Server-to-server call to the bank-account payout endpoint. |
Do not multiply by 100. amount: 50000 is fifty thousand won. Sending 5000000
would be ₩5,000,000.
Signing credentials from Getting started, a pay-in
gate_id for Korean bank transfer, and a pay-out
cascade_id enabled for KRW bank payouts.
Pay-in — hosted bank-transfer page
-
Create the invoice
Call the gate endpoint with your
gate_idand the amount in KRW. -
Redirect the payer
Send the browser to
invoice_url. Our page shows the payer the destination bank account and the reference to use for their transfer. -
Receive the result
We confirm the transfer and call your
callback_url; the invoice moves topaid.
/api/invoice{
"gate_id": "kr-bank-transfer-gate",
"invoice": {
"invoice_id": "order-kr-2026-000123",
"currency": "KRW",
"amount": 50000,
"description": "Premium plan",
"ttl_minutes": 30
},
"customer": {
"id": "cust-55021",
"full_name": "Kim Min-jun",
"phone_number": "+821000000000"
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks",
"return_success_url": "https://merchant.example.com/orders/123/done",
"return_decline_url": "https://merchant.example.com/orders/123/retry"
}
}
Python
resp = signed_post("/api/invoice", {
"gate_id": "kr-bank-transfer-gate",
"invoice": {
"invoice_id": "order-kr-2026-000123",
"currency": "KRW",
"amount": 50_000, # ₩50,000 (KRW has 0 decimals)
"description": "Premium plan",
"ttl_minutes": 30,
},
"customer": {
"id": "cust-55021",
"full_name": "Kim Min-jun",
"phone_number": "+821000000000",
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks",
"return_success_url": "https://merchant.example.com/orders/123/done",
"return_decline_url": "https://merchant.example.com/orders/123/retry",
},
})
redirect_url = resp.json()["invoice_url"]
Response
{
"request_status": "success",
"invoice_id": "9d1f2a7c-4b88-4c0e-9f2a-2d6e1c4b7e10",
"merchant_invoice_id": "order-kr-2026-000123",
"invoice_status": "unpaid",
"invoice_url": "https://stage.example-mp.com/public/invoice/9d1f2a7c-.../gate",
"message": "Invoice (Gate PayIn) with internal uid=9d1f2a7c-... has been created."
}
Tracking the pay-in
/api/invoice/statusresp = signed_post("/api/invoice/status", {
"merchant_invoice_id": "order-kr-2026-000123"
})
invoice = resp.json()
# invoice["status"] -> "paid" once the transfer is confirmed
# "expired" if the payer doesn't transfer before ttl_minutes elapses
The payer leaves to complete a transfer in their banking app, so the invoice can sit in
unpaid/paying for a while. Lean on the callback rather than tight polling,
and set a ttl_minutes that matches how long you'll hold the order.
Pay-out — to a bank account
Korean payouts go to a bank account. Identify the destination bank with a neutral
bank_title slug (the routed provider maps it to the bank's own code), plus the account number and
holder name.
/api/payment/payout/bank_account{
"cascade_id": "kr-bank-payout",
"payment": {
"payment_id": "payout-kr-2026-000045",
"currency": "KRW",
"amount": 120000,
"description": "Seller settlement"
},
"bank_account": {
"bank_title": "shinhan_bank",
"account_number": "11012345678901",
"full_name": "Kim Min-jun"
},
"customer": {
"id": "seller-9182",
"full_name": "Kim Min-jun"
},
"workflow_hooks": {
"callback_url": "https://merchant.example.com/mp/callbacks"
}
}
For Korea, name the recipient bank with bank_title. The value is one of the neutral
bank slugs in the table below — e.g. "shinhan_bank" — not the
display name and not a numeric Korean bank code like "088". An
unrecognised value is rejected as invalid_request.
resp = signed_post("/api/payment/payout/bank_account", {
"cascade_id": "kr-bank-payout",
"payment": {
"payment_id": "payout-kr-2026-000045",
"currency": "KRW",
"amount": 120_000,
"description": "Seller settlement",
},
"bank_account": {
"bank_title": "shinhan_bank", # neutral bank slug from the list below
"account_number": "11012345678901",
"full_name": "Kim Min-jun",
},
"customer": {"id": "seller-9182", "full_name": "Kim Min-jun"},
"workflow_hooks": {"callback_url": "https://merchant.example.com/mp/callbacks"},
})
payout_uid = resp.json()["payment_id"]
Supported banks
Pass one of these slugs as the recipient bank_title. The list is generated from the
platform's live Korea catalogue, so it always matches what the API accepts; it also includes wallet apps
(KakaoPay, Toss, NaverPay, …). If a bank you need is missing, ask your account manager.
| Bank / wallet | 한국어 | bank_title (send this) |
|---|---|---|
| Shinhan Financial Group | 신한 | shinhan_financial_group |
| Toss bank | 토스뱅크 | toss_bank |
| Shinhan | 신한은행 | shinhan |
| NongHyup | NH농협은행 | nonghyup |
| Industrial Bank of Korea | 기업은행 | industrial_bank_of_korea |
| KEB Hana Bank | KEB하나은행 | keb_hana_bank |
| Woori | 우리은행 | woori |
| Jeonbuk Bank | 전북은행 | jeonbuk_bank |
| Hanpass | 한패스 | hanpass |
| NongHyup(Samsung) | 농협은행 | nonghyup_samsung |
| Kookmin Bank | KB국민은행 | kookmin_bank |
| KEB Hana Bank(SMS) | 하나은행 | keb_hana_bank_sms |
| Gwangju Bank | 광주은행 | gwangju_bank |
| Shinhan Bank | 신한은행 | shinhan_bank |
| KEB Hana Bank(Samsung) | 하나은행 | keb_hana_bank_samsung |
| Korea (all banks) | 전체은행 | korea_all |
| KEB Hana(Original) | 하나은행 | keb_hana_original |
| Jeonbuk Bravo | 브라보 | jeonbuk_bravo |
| NongHyup Push | 농협은행 | nonghyup_push |
| NongHyup Smart Banking | 농협은행 | nonghyup_smart_banking |
| National Credit Union Federation | 신협중앙회 | national_credit_union_federation |
| Cross | 크로스 | cross |
| Kyongnam Bank | 경남은행 | kyongnam_bank |
| KakaoPay | 카카오페이 | kakaopay |
| Suhyup Bank | 수협은행 | suhyup_bank |
| Toss Bank | 토스뱅크 | toss_bank_2 |
| Hana Bank | 하나은행 | hana_bank |
| KakaoBank | 카카오뱅크 | kakaobank |
| Daegu Bank | 대구은행 | daegu_bank |
| Busan Bank | 부산은행 | busan_bank |
| Jeju Bank | 제주은행 | jeju_bank |
| Korea Development Bank | 산업은행 | korea_development_bank |
| Korean Federation of Community Credit Cooperatives | 새마을금고 | kfcc |
| Savings Bank | 상호저축은행 | savings_bank |
| Korea Post Bank | 우체국 | korea_post_bank |
| Citibank Korea | 씨티은행 | citibank_korea |
| Bank of Korea | 한국은행 | bank_of_korea |
| Korea Exim Bank | 한국수출입은행 | korea_exim_bank |
| Forestry Cooperative Bank | 산림조합중앙회 | forestry_cooperative_bank |
| Standard Chartered Korea | SC제일은행 | standard_chartered_korea |
| K Bank | 케이뱅크 | k_bank |
| i-ONE Bank | 기업은행 | i_one_bank |
| NaverPay | 네이버페이 | naverpay |
| Korea Post Bank | 우체국은행 | korea_post_bank_2 |
| KBank | 케이뱅크 | kbank |
| NH Local Agri & Livestock Coop | NH지역농.축협 | nh_local_agri_livestock_coop |
| Saemaul Credit Union | 새마을금고중앙회 | saemaul_credit_union |
| National Credit Union Federation | 신협중앙회 | national_credit_union_federation_2 |
Tracking the pay-out
/api/payment/statusresp = signed_post("/api/payment/status", {
"merchant_payment_id": "payout-kr-2026-000045"
})
# resp.json()["status"] -> "success" | "processing" | "decline" | "error"
Testing & go-live
- On stage, drive a pay-in to
paidand a payout tosuccessusing the test IDs. - Double-check KRW amounts: 0 decimal places — the integer is the won amount as-is.
- Send a
bank_titleslug from the supported-banks list (not the display name, not a numeric code); validateaccount_numbertoo — a wrong account is a harddecline. - Confirm your callback handler verifies the
X-MP-Signatureand tolerates retries.