MagicPayments Integration Guides

South Korea P2P Gate

Pay-inPay-outKRW

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.

OperationRailHow the integration looks
Pay-inBank transfer (P2P)Create an invoice, redirect the payer to the hosted page.
Pay-outBank accountServer-to-server call to the bank-account payout endpoint.
KRW has no minor units

Do not multiply by 100. amount: 50000 is fifty thousand won. Sending 5000000 would be ₩5,000,000.

Prerequisites

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

  1. Create the invoice Call the gate endpoint with your gate_id and the amount in KRW.
  2. 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.
  3. Receive the result We confirm the transfer and call your callback_url; the invoice moves to paid.
POST/api/invoice
Request body
{
  "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

POST/api/invoice/status
resp = 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
Bank transfers are not instant

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.

POST/api/payment/payout/bank_account
Request body
{
  "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"
  }
}
Use bank_title with a slug from the list — not a numeric code

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.

Python
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
NongHyupNH농협은행nonghyup
Industrial Bank of Korea기업은행industrial_bank_of_korea
KEB Hana BankKEB하나은행keb_hana_bank
Woori우리은행woori
Jeonbuk Bank전북은행jeonbuk_bank
Hanpass한패스hanpass
NongHyup(Samsung)농협은행nonghyup_samsung
Kookmin BankKB국민은행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 KoreaSC제일은행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 CoopNH지역농.축협nh_local_agri_livestock_coop
Saemaul Credit Union새마을금고중앙회saemaul_credit_union
National Credit Union Federation신협중앙회national_credit_union_federation_2

Tracking the pay-out

POST/api/payment/status
resp = signed_post("/api/payment/status", {
    "merchant_payment_id": "payout-kr-2026-000045"
})
# resp.json()["status"] -> "success" | "processing" | "decline" | "error"

Testing & go-live