モバイル SDK を利用して 3D セキュアを導入する

モバイル SDK を利用する場合、3D セキュアを導入するにあたって、このガイドで説明する手順が必要となります。

今般のクレジット不正利用の急増を受け、経済産業省より、「2025年3月末日までに 3D セキュアの導入を義務化」する旨が通達されております。PAY.JP の加盟店様はみなさま義務化の対象となります。 お知らせページにてご案内しております。詳細は下記をご参照ください。
2025年3月末まで:EMV 3D セキュア導入義務化に伴うご対応のお願い

実装の前に

まず、トークン作成時に 3D セキュアを要求するには、管理画面からトークン3Dセキュアオプションを有効にしてください。

モバイル SDK を利用する場合、さらにリダイレクトURL設定にアプリ用のリダイレクト URL の設定を追加します。

モバイル SDK では、カード会社が提供する 3D セキュア認証画面を表示するためにブラウザアプリを利用します。 認証を実施した後、モバイルアプリで再度処理を継続するためにリダイレクト URL とその識別子を登録します。 URL スキームは HTTPS 以外にカスタム URL スキームを利用できます。 HTTPS スキームの URL の場合、iOS では Universal Link、Android では App Links の設定を行ってください。

管理画面での設定が終わったら、利用する SDK に応じて設定を追加してください。

payjp-iosの場合 

XcodeでURLスキームを設定する

管理画面で追加したリダイレクト URL の設定に合わせて、アプリにも URL スキームの設定をします。

参考: Defining a Custom URL Scheme for Your App | Apple Developer Documentation

Xcode を開き、プロジェクト設定でアプリのターゲットを選択し Info タブに切り替えます。 URL Types という項目の + ボタンからスキーム設定を追加し、管理画面で登録したリダイレクト URL のスキームを設定します。

たとえば、リダイレクト URL が jp.pay.example://tds/finish の場合、以下のように jp.pay.example を設定します。

リダイレクトURLを登録する

管理画面で指定したリダイレクト URL と識別子を登録します。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {

    PAYJPSDK.publicKey = YourPayjpPublicKey
    PAYJPSDK.locale = Locale.current
    PAYJPSDK.threeDSecureURLConfiguration =
        ThreeDSecureURLConfiguration(redirectURL: URL(string: "jp.pay.example://tds/finish")!,
                                     redirectURLKey: "mobileapp")

    return true
}

カスタム URL スキームによってアプリが起動された際、 ThreeDSecureProcessHandlercompleteThreeDSecureProcess(url:) を呼び出すことで、認証フローを完了します。

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return ThreeDSecureProcessHandler.shared.completeThreeDSecureProcess(url: url)
}

カード所有者を3Dセキュアに誘導する

カードフォーム画面を利用している場合

CardFormViewController.createCardFormViewController() を利用してカードフォーム画面を呼び出している場合は、SDK が自動的にハンドリングするため、カード所有者を 3D セキュアに誘導するための追加の実装は不要です。

それ以外の場合

createToken によって取得した TokenToken.card.threeDSecureStatus.unverified の場合、3D セキュアの認証を開始してください。

まず、 ThreeDSecureProcessHandler.startThreeDSecureProcess(viewController:delegate:token:) の第3引数に取得した Token を渡します。

これにより、SDK はブラウザを開きカード所有者を認証画面へと誘導します。認証が完了、失敗、またはキャンセルによって終了するまで、SDK によるトークン作成処理は中断状態となります。

self.cardFormView.createToken() { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .success(let token):
        if tdsStatus = token.card.threeDSecureStatus, status == .unverified {
            ThreeDSecureProcessHandler.shared.startThreeDSecureProcess(viewController: self,
                                                                       delegate: self,
                                                                       token: token)
        } else {
            // 取得したトークンを扱う
            self.onSuccess(token)
        }
    case .failure(let error):
            // エラー処理
        }
    }
}

認証が終了し、リダイレクト URL によって ThreeDSecureProcessHandler.completeThreeDSecureProcess(url:) が呼び出されることで、SDK は認証フローの終了を検知します。 ThreeDSecureProcessHandlerDelegate をアプリの ViewController に適合し、 threeDSecureProcessHandlerDidFinish(_:status:) で受け取る ThreeDSecureProcessStatus に応じてトークン作成処理を完了させてください。

public func threeDSecureProcessHandlerDidFinish(_ handler: ThreeDSecureProcessHandler,
                                                status: ThreeDSecureProcessStatus) {
    switch status {
    case .completed:
        // 3DSの処理を完了する
        completeTokenTds()
    case .canceled:
        // UI更新など
    default:
        break
    }
}

ThreeDSecureProcessStatus.completed の場合、先に取得した Token を利用して認証済みの Token を取得できます。 APIClient.finishTokenThreeDSecure(tokenId:completion:) を利用して再取得を行ってください。

let completion: (Result<Token, APIError>) -> Void = { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .success(let token):
        self.onSuccess(token)
    case .failure(let error):
        self.onFailure(error)
    }
}
if let token = self.pendingToken {
    APIClient.shared.finishTokenThreeDSecure(tokenId: token.identifer, completion: completion)
}

以上でトークン作成時に 3D セキュアを要求する iOS の実装は完了です。

payjp-androidの場合

リダイレクトURLを登録する

管理画面で指定したリダイレクト URL と識別子を登録します。

Payjp.init(PayjpConfiguration.Builder(YOUR_PUBLIC_KEY)
    .setThreeDSecureRedirectName("mobileapp") // URLではなく識別子を登録します
    .build())

AndroidManifest.xmlにIntentFilterを追加する

アプリの AndroidManifest.xml に管理画面で指定したリダイレクト URL を追加します。アクティビティとして jp.pay.android.verifier.ui.PayjpThreeDSecureStepActivity を追加し、IntentFilter を設定します。

たとえば、リダイレクト URL が my-app://tds/complete の場合、以下のように記述します。

<activity android:name="jp.pay.android.verifier.ui.PayjpThreeDSecureStepActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="my-app" android:host="tds" android:path="complete" />
    </intent-filter>
</activity>

カード所有者を3Dセキュアに誘導する

カードフォーム画面を利用している場合

Payjp.cardForm().start() を利用してカードフォーム画面を呼び出している場合は、SDK が自動的にハンドリングするため、カード所有者を 3D セキュアに誘導するための追加の実装は不要です。

それ以外の場合

createToken によって取得した TokenToken.card.threeDSecureStatusUNVERIFIED のとき、3D セキュアの認証を開始してください。

まず、 Token.retrieveId() で得られる TokenId を、 Payjp.verifier().startThreeDSecureFlow(TokenId, Activity) の第1引数に渡します。

これにより、SDK はブラウザを開きカード所有者を認証画面へと誘導します。認証が完了、失敗、またはキャンセルによって終了するまで、SDK によるトークン作成処理は中断状態となります。

以下の例は PayjpCardFormFragment#createToken を利用した場合です。

cardFormFragment.createToken().enqueue(object : Task.Callback<Token> {
    override fun onSuccess(data: Token) {
        if (data.card.threeDSecureStatus == ThreeDSecureStatus.UNVERIFIED) {
             Payjp.verifier()
                .startThreeDSecureFlow(data.retrieveId(), this@SampleActivity)
        } else {
            sendTokenToServer(token: data)
        }
    }

    override fun onError(throwable: Throwable) {
        updateUI(throwable)
    }
})

認証が終了すると、Payjp.verifier().startThreeDSecureFlow(TokenId, Activity) の第2引数に渡した呼び出し元の Activity(または Fragment)で onActivityResult に結果を受け取ります。

Payjp.verifier().handleThreeDSecureResult(requestCode, callback) を使って認証フローの結果を取り出し、コールバックで渡される PayjpThreeDSecureResult に応じて処理を再開してください。 PayjpThreeDSecureResult#isSuccess() が true の場合は認証フローが完了していることを表します。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    Payjp.verifier().handleThreeDSecureResult(requestCode) { result ->
        if (result.isSuccess()) {
            this.onCompleteTds(result)
        } else {
            Toast.makeText(this, "3-D Secure canceled.", Toast.LENGTH_SHORT).show()
        }
    }
}

3D セキュアの認証に成功した場合、Payjp.verifier().completeTokenThreeDSecure(result) で認証済みの Token を取得します。

if (!result.isSuccess()) {
  return
}
Payjp.token().completeTokenThreeDSecure(result)
    .enqueue(object : Task.Callback<Token> {
        override fun onSuccess(data: Token) {
            Log.i("PAY.JP", "token => $data")
        }
        override fun onError(throwable: Throwable) {
            Log.e("PAY.JP", "failure getting token", throwable)
        }
    })

以上でトークン作成時に 3D セキュアを要求する Android の実装は完了です。

payjp-react-native-pluginの場合

カードフォーム画面を利用する方法以外はサポートしていません。

各プラットフォームごとにリダイレクトの設定をする

iOS では、payjp-ios同様、XcodeでURLスキームを設定します。 Android では、payjp-android同様、AndroidManifest.xmlにIntentFilterを追加します。

リダイレクトURLを登録する

管理画面で設定したリダイレクト URL と識別子を登録します。 PayjpCore#init の引数となるオブジェクトに threeDSecureRedirect というキーで下記のように URL と識別子を指定してください。

PayjpCore.init({
    publicKey: YOUR_PUBLIC_KEY,
    threeDSecureRedirect: {
        url: "jp.pay.example://tds/finish",
        key: "mobileapp",
    },
});

payjp-flutter-pluginの場合

カードフォーム画面を利用する方法以外はサポートしていません。

各プラットフォームごとにリダイレクトの設定をする

iOS では、payjp-ios同様、XcodeでURLスキームを設定します。 Android では、payjp-android同様、AndroidManifest.xmlにIntentFilterを追加します。

リダイレクトURLを登録する

管理画面で設定したリダイレクト URL と識別子を登録します。 Payjp#init の引数となるオブジェクトに threeDSecureRedirect というキーで下記のように URL と識別子を指定してください。

Future<void> _initPayjp() async {
  await Payjp.init(
    publicKey: YOUR_PUBLIC_KEY,
    threeDSecureRedirect: PayjpThreeDSecureRedirect(
      url: 'jp.pay.example://tds/finish', key: 'mobileapp'));
}

まとめ

モバイル SDK を利用して 3D セキュアを導入する手順は以下のとおりです。

  1. 管理画面でトークン 3D セキュアオプションを有効にし、リダイレクト URL と識別子を設定する
  2. 各 SDK ごとに必要な設定を行う
    • iOS の場合は Xcode で URL スキームを設定
    • Android の場合は AndroidManifest.xml に IntentFilter を追加
  3. SDK の初期化時にリダイレクト URL と識別子を登録する
  4. トークン作成後、3D セキュアが必要な場合は SDK の機能を使ってカード所有者を認証フローに誘導する
  5. 認証後のリダイレクトを受け取り、SDK の機能で認証済みのトークンを取得する

これらの手順に沿って実装を行うことで、モバイルアプリでも安全なクレジットカード決済を提供できます。 セキュリティ強化のため、ぜひ 3D セキュアの導入をご検討ください。

リファレンス