Skip to content

Азия: провайдеры и банки ​

Это примеры, а не готовые пакеты

Эта страница показывает, как можно самостоятельно подключить этих провайдеров. Готовые пакеты есть только для Stripe, Adyen и PayPal — см. список пакетов. Если провайдер здесь упомянут, это не значит, что checkout kit уже его поддерживает.

English version: asia.md

Сначала прочитайте Настоящие провайдеры на нашем контракте.

Как устроен регион ​

Азия — это место, где чекаут «под карту» перестаёт работать. В Китае платят через Alipay и WeChat Pay. В Индии UPI проводит больше транзакций, чем карты. В Индонезии, Таиланде и Вьетнаме правят QR-стандарты и банковские переводы; в Японии до сих пор обычное дело — оплата в круглосуточном магазине и списание со счёта мобильного.

Почти всё это укладывается в две формы:

  • Передача в приложение. На телефоне открываем платёжное приложение по диплинку. Покупатель подтверждает там и возвращается.
  • Показать код и ждать. На десктопе показываем QR, который покупатель сканирует телефоном. Перенаправлять некуда, собирать с него нечего.

Первое ложится на действие redirect, второе — на display. Про оба написано ниже на этой странице.

Карта ​

ПровайдерГдеСхемаДействие
Alipay / Alipay+Китай и трансграничнокод заказа, затем опросdisplay + опрос
WeChat PayКитайprepay id, QR или JSAPIdisplay + опрос
RazorpayИндияorder + checkout SDKsdk_handoff
PayU India, PaytmИндияhosted page или SDKredirect / sdk_handoff
UPIИндиядиплинк или QRredirect / показ
Midtrans, XenditИндонезиятранзакция + перенаправление или VAredirect / показ
Omise, 2C2PТаиланд, ЮВАтокен + charge, PromptPay QRsdk_handoff / показ
GrabPay, ShopeePayЮВАзаказ + диплинкredirect
Toss, KakaoPayКореяready + перенаправление + approveredirect
PayPay, Komoju, GMOЯпонияперенаправление или оплата в магазинеredirect / показ
Stripe, AdyenJP, SG, HK, AUкак вездеredirect / sdk_handoff

Razorpay (Индия) ​

Самая частая интеграция в Индии и чистый sdk_handoff.

Бэкенд создаёт заказ. Браузер открывает чекаут Razorpay с этим id, покупатель платит через UPI, карту, нетбанкинг или кошелёк, и SDK отдаёт три поля:

ts
action: {
  id: order.id,
  kind: 'sdk_handoff',
  purpose: 'authorize',
  surface: 'none',
  sdk: 'razorpay',
  scriptUrl: 'https://checkout.razorpay.com/v1/checkout.js',
  completion: { via: 'sdk_callback' },
  params: { key: ctx.config.keyId, orderId: order.id, amount: intent.amount },
}

Адаптер открывает окно оплаты и возвращает то, что пришло в handler:

ts
{
  razorpay_payment_id: 'pay_29QQoUBi66xm2f',
  razorpay_order_id: 'order_9A33XWu170gUtm',
  razorpay_signature: '9ef4dffbfd84f1318f6739a3ce19f9d85851857ae648f114332d8401e0949a3d',
}

Эта подпись — весь смысл, и проверять её надо на сервере. Это HMAC от order_id|payment_id с вашим key secret. Плагин проверить её не может: секрета у него нет и быть не должно. Поэтому resume отправляет все три поля на бэкенд и возвращает то, что ответит бэкенд:

ts
resume: async (intentId, evidence, opts) => {
  if (evidence.via !== 'sdk_callback') return unsupported(evidence)
  return toResult(await http.post(`/razorpay/orders/${intentId}/verify`, evidence.payload, opts))
}

Это самый наглядный пример правила «evidence — подсказка» во всём репозитории: у браузера на руках подписанное сообщение, и решает всё равно сервер.

Midtrans и Xendit (Индонезия) ​

Оба дают URL страницы оплаты — redirect + surface: 'top' + return_url, — и оба предлагают виртуальные счета: покупателю выдают номер счёта, на который он переводит деньги, возможно завтра. Виртуальный счёт — это сценарий «показать и ждать», как и QR.

Проще всего начать с invoice API у Xendit: создать инвойс, перенаправить на invoice_url, а в resume прочитать его статус.

Omise и 2C2P (Таиланд и ЮВА) ​

Omise токенизирует карту своим JavaScript (sdk_handoff, результат — токен, списание на бэкенде) и предлагает PromptPay как QR — снова «показать и ждать».

2C2P — история для крупного бизнеса и обычно hosted page: redirect + return_url.

Toss Payments и KakaoPay (Корея) ​

У корейских API схема из трёх шагов: ready, approve, confirm.

  1. Бэкенд вызывает «ready» и получает адрес.
  2. Покупателя перенаправляют туда, он подтверждает.
  3. Он возвращается с ключом, и бэкенд вызывает «approve» с этим ключом.

Это ложится на redirect + return_url, а resume делает approve. Как и с PayPal, деньги двигает именно approve, поэтому сам по себе возврат на вашу страницу ничего не доказывает.

Alipay и WeChat Pay (Китай) ​

Со стороны продавца оба работают одинаково. Вы создаёте заказ и получаете код — QR для десктопа или диплинк для телефона. Покупатель платит в приложении. Бэкенд узнаёт об этом вебхуком, а если вебхук не пришёл — опрашивает API проверки статуса.

Для диплинка плагин уже сегодня умеет всё нужное:

ts
action: {
  id: order.id,
  kind: 'redirect',
  purpose: 'authorize',
  surface: 'top',
  url: order.deepLink,     // на телефоне откроет приложение
  method: 'GET',
  completion: { via: 'return_url' },
}

Про QR — см. «Показать код и ждать» ниже.

Учтите ещё, что покупатель может вообще не вернуться на вашу страницу: он платит в приложении и переключается дальше. Платёж при этом состоялся. Ещё одна причина, почему исход должен приходить от вашего API, а не от того, что браузер вернулся.

Показать код и ждать ​

Так платит бо́льшая часть Азии — Alipay, WeChat, PromptPay, UPI QR, индонезийские виртуальные счета, — а ещё PIX в Бразилии и BLIK в Польше. В контракте для этого есть свой вид действия:

ts
{
  id: intentId,
  kind: 'display',
  surface: 'inline',
  format: 'qr',             // или 'code', или 'instructions'
  value: '00020126...',     // сам payload, всегда — см. ниже
  imageUrl: qrFromProvider, // необязательно: своего кодировщика QR в наборе нет
  deeplink: 'upi://pay?...',// необязательно: открывает приложение на телефоне
  completion: { via: 'poll', intervalMs: 2000, timeoutMs: 15 * 60 * 1000 },
}

Движок показывает это и одновременно опрашивает ваш getIntent. Кто закончил первым — останавливает другого: покупатель может уйти, а истёкший код перестаёт опрашиваться. Если выиграл опрос, вызывается resume с evidence { via: 'poll' } и перечитывает исход у вас.

Два момента:

  • value заполняйте всегда. Покупатель, читающий страницу с телефона, не отсканирует собственный экран — код для копирования тут важнее самого QR.
  • Ставьте свои таймауты. QR или виртуальный счёт живут часами, и движок слушает timeoutMs из действия, а не своё значение по умолчанию.

@checkout-kit/provider-bank-transfer — рабочий плагин такой формы, и по нему гоняются те же тесты Playwright.

На телефоне диплинк обычно удобнее QR: для него по-прежнему подходит redirect с surface: 'top', и провайдеры, у которых есть оба варианта, дают URL для одного и payload для другого.

Банки в Азии ​

Прямые интеграции слишком разные, чтобы обобщать, но три вещи встречаются постоянно:

  • Банковский перевод с номером-ссылкой. Покупатель переводит вручную и указывает код. «Показать и ждать», причём ждать можно сутки.
  • Локальные карточные схемы. RuPay в Индии, JCB в Японии, UnionPay по всему региону. Для контракта это обычные карты; меняется то, какие BIN принимает ваш PSP и какая у них аутентификация.
  • Региональный 3-D Secure. У RuPay и UnionPay свои схемы аутентификации, но и они сводятся к «отправь что-то на URL и получи вердикт» — то есть к действию redirect с завершением через post_message или return_url.

Что стоит знать на практике ​

Валюты без копеек. У японской иены, корейской воны и вьетнамского донга нет минорной единицы. amount: 1000 — это ¥1000, а не ¥10.00. Ошибка здесь стократная, и это самый частый баг в первой азиатской интеграции.

Покупатель может не вернуться. Сценарии на телефоне часто заканчиваются в другом приложении. Никогда не завязывайте финальный статус на возврат браузера.

Таймауты длиннее. QR или виртуальный счёт могут жить часами. У движка таймаут опроса по умолчанию — минута; для страницы чекаута это правильно, а для всего платежа нет: покупатель должен иметь возможность уйти, а бэкенд — сообщить исход позже.

Apache-2.0 · Headless-движок, адаптеры провайдеров и запускаемые примеры.