モバイル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によって得られるTokenの Token.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'));
}

リファレンス