Tapaya はある午後だけで、どうやって POS アプリに店頭カード決済を追加したか

ExpoでPOSアプリを作り、Tapaya SDKでカード決済を連携、アイデアから初回決済承認まで半日以内。

日本語
コピー
How Tapaya added in-store card payments to an Expo POS app in an afternoon

この記事は Roman Kuchařík が執筆した。同氏は tapaya. の共同創業者。プラハに拠点を置くこの会社は、あらゆるモバイル端末を安全な決済端末に変える純ソフトウェア型 SoftPOS インフラを開発している。

POS アプリを開発するチームは、遅かれ早かれ同じ壁にぶつかる。店頭でのカード決済をどうやって受け付けるか、という問題だ。

プロダクトは動いている。加盟店も試してくれる。ロードマップも明確だ。ところが決済を組み込む段階になった途端、話は何倍も複雑になる。PCI 準拠、EMV 認証、アクワイアラとの関係構築、ハードウェア依存、そして長い開発サイクル。実際にリリースするまでには、まだ長い道のりが残っている。

Tapaya では、この流れはもっとずっとシンプルであるべきだと考えている。

そこでひとつ試してみることにした。Expo で本物の POS アプリを作り、自社 SDK 経由で店頭のカード決済をつなぎ込み、どれだけ速くできるかを確かめるのだ。

結果、アイデアを思いついてから最初の決済が承認されるまで、半日もかからなかった。

この記事で扱うのは次のとおり。

  • このアプリの着想はどこから来たのか

  • 何を作ったのか

  • そして、どうやってカード決済を Expo アプリに組み込んだのか

Tapaya アプリ

このアプリのアイデアはどこから来たのか

最初のアイデアはシンプルだった。

近所でよく行くカフェ向けに、軽量な POS システムを作りたかった。速くて、ミニマルで、普通のスマホで使えて、従来の決済ハードウェアを必要としないもの。

小規模な店舗の多くは、決済のために端末やケーブル、充電ドック、複数のシステムを管理したくない。一台のデバイスで完結させたいだけだ:

  • 商品の管理

  • 取引の処理

  • マーチャントのオンボーディング

  • タッチ決済の即時受け付け

さらに、もっと大きな問いを社内で検証したかった:

開発者は、決済インフラに何ヶ月も費やすことなく、Expo で本番運用可能な SoftPOS 体験を作れるのか?

これがその実験になった。そして幸運なことに、STRV と Expo がプラハで主催する Hackathon で、この実験をちょうど試す機会を得た。

プラハのハッカソン

私たちが構築したのはどのようなアプリか?

Expo でシンプルなクロスプラットフォーム POS アプリを構築した。

カフェアプリ

このアプリには次のものが含まれる:

  • 商品在庫画面

  • 会計フロー

  • 取引履歴

  • 加盟店オンボーディング(KYB)

  • オフラインでのカードタッチ決済

目標は複雑なエンタープライズ向け POS システムを作ることではなく、モダンな決済インフラを React Native アプリにどれだけ速く組み込めるかを示す、クリーンで動作するプロトタイプを作ることだ。

午後が終わるころには、このアプリはすでに以下をこなせるようになっていた:

  • 加盟店オンボーディングを完了する

  • Tapaya プラットフォーム経由で認証する

  • 非接触型カード決済を受け付ける

  • NFC 対応の iPhone または Android 端末上で、サンドボックス環境で承認済みの取引を直接処理する

外部の決済端末は不要だ。

なぜ Expo を選んだか

着手前にいくつかのフレームワークとワークフローを評価し、最終的に Expo を選んだ。決済に必要なネイティブモバイル側の設定を大幅に簡略化してくれるからだ。

Expo Config Plugin がネイティブ SDK の組み込みを簡単にする

app.json にconfig plugin を追加しexpo prebuild をもう一度実行すれば、iOS と Android のネイティブバインディングが自動生成される。

Swift や Kotlin を手で書く必要はない。

EAS Build が iOS Tap to Pay の entitlement を簡単にする

Apple Tap to Pay を手動で設定するのはすぐに面倒になる。Expo の Services が provisioning と entitlement の手続きの大部分を処理してくれた。

EAS Update がイテレーションを加速する

テスト中はUI とロジックの更新を素早くプッシュでき、App Store の審査サイクルを何度も待たずに済んだ。

Expo Router が開発を速める

次の画面を組んだ:

  • 販売

  • 取引

  • KYB オンボーディング

  • 設定

テンプレートコードはほとんど書いていない。

Tapaya の SDK は Expo のためにある

統合は Expo の config plugin 体系と期待どおりに噛み合い、設定は驚くほどすんなり進んだ。

アプリはこうやって組んだ:

午前中に Expo で POS の中核画面をまず作り上げた:

  • シンプルな在庫管理

  • 会計画面

  • 取引処理

UI フローが動くようになってから、オフライン決済の組み込みに移った。

開発を速めるため、AI コーディングエージェントに次の材料を渡した:

expo prebuild を実行し、実機にデプロイし、決済カードをタッチしたところで、最初の承認済み取引が取れた。

登録からサンドボックス決済の成功まで、統合にかかった時間はおよそ 30 分だった。


統合の流れはこうだ:

1. Config Plugin を追加する

{
 "expo": {
   "plugins": [
     ["@tapayadot/accept-react-native"]
   ]
 }
}

2. SDK をインストールしてネイティブコードを生成する

npm install @tapayadot/accept-react-native@latest

npx expo prebuild --clean

3. バックエンドを組む

モバイルアプリが SDK で認証を完了するには、まずバックエンドが加盟店ごとに短命なログイントークンを発行する必要がある。

流れは次のとおり:

モバイルアプリがあなたのバックエンドを呼び出す

バックエンドが Server Secret Token を使って Tapaya API を呼び出す

バックエンドが一時ログイントークンをモバイルアプリに返す

Server Secret Token は絶対にクライアントに露出させてはいけない。

ステップ A:加盟店を登録する

この手順は通常、加盟店の登録時に一度だけ行う。

POST /merchant/auth/register
Authorization: Bearer YOUR_SERVER_SECRET_TOKEN
{
 "merchantToken": "your_internal_db_id",
 "merchantName": "Acme Coffee",
 "email": "owner@acme.com"
}

これで加盟店レコードが作成され、以降の SDK 認証と決済処理はこれに紐づく。

ステップ B:ログイントークンを生成する

この手順は SDK を初期化するたびに実行する。

モバイルアプリは自前のバックエンドのログインエンドポイントを呼び出し、バックエンドが Tapaya に一時ログイントークンを要求してデバイスへ転送する。

こうすれば Server Secret Token は常にサーバー側に安全に留まる。

POST /merchant/auth/login
Authorization: Bearer YOUR_SERVER_SECRET_TOKEN
{
 "merchantToken": "your_internal_db_id",
 "allowOnboarding": true
}

レスポンス

{
 "token": "EesrFq4PUK1WxHUj93hkrKASDFp8GxJ0"
}

このトークンをモバイルアプリに返し、すぐにそれを使って SDK 認証を完了させる。

このトークンは:

  • 一時的である

  • merchantToken が示す加盟店に紐づく

  • SDK を初期化するたびに再取得すべきである

  • デバイス上に長期保存すべきではない

セキュリティ上の注意

Server Secret Token はあなたの Tapaya プラットフォームアカウントへの完全なアクセス権を持つ。

安全のため:

  • モバイルアプリにハードコードしてはいけない

  • クライアントコードに露出させてはいけない

  • バージョン管理にコミットしてはいけない

  • バックエンドで環境変数として安全に保管する

  • 漏洩したら即座にローテーションする

ログイントークンを生成するときは正しい merchantToken を使う必要がある。SDK がどの加盟店アカウントと資金にアクセスできるかはこれで決まるからだ。

4. SDK を初期化する

import AcceptSDK from '@tapayadot/accept-react-native';
import { useEffect } from 'react';

export default function RootLayout() {
 useEffect(() => {
   async function boot() {
     await AcceptSDK.initialize(true);

     const merchantToken = await myBackend.login();

     await AcceptSDK.authenticate(merchantToken);
   }

   boot();
 }, []);

 return <Slot />;
}

5. カード決済を実行する

import AcceptSDK, { CardPaymentIntent } from '@tapayadot/accept-react-native';

import * as Crypto from 'expo-crypto';

async function handleCharge(amountCents) {
 const intent = {
   paymentIntentId: Crypto.randomUUID(),
   amount: amountCents,
   requestedCurrency: 'USD',
 };

 const result = await AcceptSDK.payments.startCardPayment(
   intent,
   (status) => console.log('Status:', status),
   (msg, err) => console.error(msg, err),
 );

 return result;
}

決済フロー全体は結局 3 つの中核関数に集約される:

  • initialize

  • authenticate

  • startCardPayment

マーチャントのオンボーディングと KYB

決済の受け付けは、実際の POS ワークフローのほんの一部にすぎない。

マーチャントが取引を処理するには、オンボーディングと KYB 検証も完了させる必要がある。

SDK にはオンボーディングフローが組み込まれている:

import AcceptSDK from '@tapayadot/accept-react-native';

await AcceptSDK.identity.presentKyb();
// or via REST API and Webhooks
// or via Tapaya Platform on the Web

支払いの処理

オンボーディングが完了すると、startCardPayment() がネイティブの tap to pay 画面を開く。

const result = await AcceptSDK.payments.startCardPayment(
 {
   paymentIntentId: Crypto.randomUUID(),
   amount: 15000,
   requestedCurrency: 'USD',
 },
 (status) => setPaymentStatus(status),
 (msg, err) => setError(`${msg}: ${err}`),
);

if (result.status === 'APPROVED') {
 router.push('/receipt');
}

金額は最小通貨単位で指定する:

15000 = 150.00 USD

Tapaya アプリ

Tapaya が裏側で処理していること

統合インターフェースは意図的に最小限に抑えられているが、SDK はその裏で大量の複雑さを包み込んでいる。具体的には:

  • EMV カーネルと NFC 通信

  • Apple Tap to Pay の権限

  • アクワイアラ接続

  • カードブランドのルーティング

  • KYB オンボーディング

  • 決済コンプライアンス基盤

これにより、プロトタイプから実際にリリースできるオフライン決済までの時間が大幅に短縮される。

クイックスタート

動作する統合を構築するには:

pm install @tapayadot/accept-react-native@latest
  • app.json に config plugin を追加する

  • 実行する:

px expo prebuild --clean
  • 以下を実装する:

    • initialize

    • authenticate

    • startCardPayment

  • 実機でアプリを実行する

デバイス要件

Android

  • Android 11 以上

  • NFC 対応

  • ハードウェア keystore が有効

  • デバイスが root 化されていない

iPhone

  • iPhone XS 以降

  • iOS 18 以上

  • 脱獄していない

次にやること

決済の統合は完了したので、今は以下に注力している:

  • Bluetooth サーマルプリンターのサポート

  • Expo SQLite を使ったオフライン取引キュー

  • マーチャントツールの拡充

  • その他の POS フロー

決済基盤はすでに整っているので、決済レールを一から作り直すことなく、マーチャント体験全体の改善に集中できる。

Expo で POS を構築して分かったこと

これまで POS チームは、次のどちらかを選ばざるを得なかった:

  • 高速なクロスプラットフォーム開発

  • それとも本番品質のオフライン決済

Expo はアプリケーション層を、Tapaya は決済基盤層を簡素化する。

結局のところ、統合全体は 3 回の関数呼び出しに収まった。簡略化したデモではなく、このアプリを実際に動かしている実装だ。

この簡潔さのおかげで、SDK は AI 支援の開発フローにも非常に向いている。

そして決済基盤においては、API は小さいほどたいてい良い。

出典: Expo Blog← ホームへ戻る