PAY.JP iOS SDK はPAY.JPをiOSアプリに導入するためのSDKです。
SDKの提供する「カード情報のトークン化」機能を利用することで、カード情報を事業者さまのサーバで扱うことなく支払い処理をおこなうことができます。
ここではアプリにSDKを組み込み、クレジットカード情報をトークン化する方法について説明します。
なお、以下のサンプルコードはSwiftによって記述されています。Objective-CのサンプルコードはGitHubのリポジトリを参照ください。
payjp/payjp-ios: PAY.JP iOS SDK
SDKをつかったApple Payの導入方法については次のチュートリアルをご覧ください。
インストール
Carthage または、CocoaPods を使ってインストールします。
Carthage
github "payjp/payjp-ios"
CocoaPods
pod 'PAYJP'
SDKの初期化
はじめに、UIApplicationDelegate
クラスを継承したクラスで、PAY.JPのパブリックキーを設定してSDKを初期化します。
以下のサンプルコードではテストモードのパブリックキーを設定しています。
import PAYJP
class AppDelegate: UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
PAYJPSDK.publicKey = "pk_test_0383a1b8f91e8a6e3ea0e2a9"
PAYJPSDK.locale = Locale.current
return true
}
アプリに組み込む方法を選択する
アプリにSDKを組み込みトークンを取得する方法は以下のように複数用意されており、事業者さまの要件に合わせて選択いただけます。
1. 用意されたカードフォーム画面を利用する(推奨)
これから組み込みを始める場合、この方法を推奨します。
SDKによってコントロールされたフォーム画面を表示します。カード情報のトークン化はフォーム画面内でSDKが行うため、アプリは生成されたトークンをサーバに送信するだけという最も簡単な方法です。
フォームのスタイルは FormStyle
クラスを使用してカスタマイズ可能です。
2. アプリで用意した画面にカードフォームを組み込む
CardFormTableStyledView
, CardFormLabelStyledView
, CardFormDisplayStyledView
のいずれかをアプリの画面に追加する方法です。この場合もカードフォームはSDKが提供するので、アプリはカード情報を扱う必要がありません。
追加の支払い情報などを合わせて表示したい等、1の方法では実現できないような画面が必要な場合はこちらを推奨します。
3. SDKのUIを使わずに直接トークンを生成する
1、2いずれの方法でも要件を満たせない場合、カード情報から直接トークンを生成することも可能です。
1. カードフォーム画面を利用する場合(推奨)
STEP 1: フォーム画面を開始する
カードフォーム画面のフローを開始するには、 CardFormViewController#createCardFormViewController(style:delegate:viewType:)
を呼び出します。正常に画面が表示されない場合、SDKの初期化に問題のある可能性があります。
// ViewControllerから呼び出す
let style = FormStyle(
labelTextColor: .black,
inputTextColor: .black,
tintColor: .blue)
let cardFormViewController = CardFormViewController.createCardFormViewController(style: style, delegate: self, viewType: .labelStyled)
self.navigationController?.pushViewController(cardFormViewController, animated: true)
カードフォーム画面は3種類の表示タイプを CardFormViewType
によって指定することができます。何も指定しない場合は .labelStyled
が適用されます。フォームやボタンの色はFormStyleによって変更することができます。(見た目を変更する)
CardFormViewType | スクリーンショット |
---|---|
.tableStyled |
![]() |
.labelStyled |
![]() |
.displayStyled |
![]() |
STEP 2: フォームの結果を受け取る
カードフォーム画面を呼び出したViewControllerを CardFormViewControllerDelegate
に適合し、cardFormViewController(_:didProduced:completionHandler:)
を実装します。
func cardFormViewController(_: CardFormViewController,
didProduced token: Token,
completionHandler: @escaping (Error?) -> Void) {
print("token = \(token)")
}
STEP 3: 生成されたトークンをサーバに送信する
通常、カード情報から生成されたトークンは事業者さまのサーバに送信し、顧客データや支払いデータを作成する等といった処理を行います。 トークン取得後の一連の処理が何らかの事由(例えばネットワークエラーなど)によって中断した場合、カードフォーム画面が終了していると、再度カードフォーム画面を表示し顧客に入力を求めなければなりません。
そのため、トークン取得時に呼び出されるデリゲートを通じて、フォームを表示した状態でアプリ側からエラーメッセージを渡せる仕組みを提供しています。
STEP 2で実装した cardFormViewController(_:didProduced:completionHandler:)
の中でトークンをサーバに送信するなどハンドリングを行います。このメソッドはバックグラウンドで実行されるため、UIの操作を直接実行しないように注意してください。
処理の最後に引数の completionHandler:
を呼び出し、カードフォームに結果を反映します。
トークンの処理に問題があった場合、 completionHandler:
にはカードフォームにエラーとして表示するメッセージを渡します。問題なく完了した場合は引数を nil
として呼び出すことでカードフォームは終了します。
func cardFormViewController(_: CardFormViewController,
didProduced token: Token,
completionHandler: @escaping (Error?) -> Void) {
// サーバにトークンを送信
apiService.saveToken(withToken: token) { (error) in
if let error = error {
completionHandler(error)
} else {
completionHandler(nil)
}
}
}
カードフォームはその結果に応じて CardFormResult
を返して終了します。
結果を受け取るためには CardFormViewControllerDelegate#cardFormViewController(_:didCompleteWith:)
を実装します。
func cardFormViewController(_: CardFormViewController,
didCompleteWith result: CardFormResult) {
switch result {
case .cancel:
// カードフォームをキャンセルした場合
case .success:
// トークンの保存に成功したらカードフォームを終了する
DispatchQueue.main.async { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
}
}
以上がカードフォーム画面を使ったアプリへの組み込みの基本となります。
2. カードフォームをアプリの画面に組み込む場合
SDKでは事業者さまのアプリにそのまま組み込むことができるカードフォームを提供しています。事業者さまはカードフォームをアプリに組み込むことで、カード情報を取り扱うことなく、SDKによって安全にトークンを取得することができます。また、入力補完や入力ミスの表示などカード情報の入力に適切な顧客体験を提供します。
サンプルアプリの例(色やTextFieldのスタイルはカスタマイズ可能です。)
STEP 1: フォームの組み込み
カードフォームを表示するには CardFormTableStyledView
, CardFormLabelStyledView
, CardFormDisplayStyledView
のいずれかを利用します。CocoaPodsを使用する場合はstoryboardに各Viewを配置して利用できますが、Carthageを使用する場合はstoryboardでの利用ができません。そのため、コード上でViewをaddSubViewする形で実装します。
CocoaPodsをご利用の方は example-objc
、Carthageをご利用の方は example-swift
のサンプルコードを参照ください。
STEP 2: フォームの送信
CardFormAction#createToken
は Result<Token, Error>
を返し、生成したトークンまたはエラーを取得できます。
エラー項目については Error を参照ください。
func onClickSubmit() {
cardFormView.createToken() { result in
switch result {
case .success(let token):
print("[token] => \(token)")
case .failure(let error):
print("[failure creating token] \(error)")
}
}
}
STEP 3: フォームのバリデーション
カードフォームは入力内容が適切か入力のたびに検証します。
検証の結果のみを得るには CardFormAction#isValid
を利用します。
CardFormAction#validateCardForm
は暗黙の入力エラーを表示し、検証結果を返します。例えば、送信ボタンを押したタイミングで入力の検証を行い、未入力の項目があればエラーメッセージを表示したい場合はこちらを利用します。
また、検証結果の通知を受け取りたい場合は、ViewControllerを CardFormViewDelegate
に 適合し、 formInputValidated(in:isValid:)
を実装します。
func formInputValidated(in cardFormView: UIView, isValid: Bool) {
createTokenButton.isEnabled = isValid
}
フォームをカスタマイズする
クレジットカード情報をカメラで読み取る
カメラでクレジットカードの券面をスキャンして自動で入力する機能を追加できます。
この機能を利用するには、Card IOのiOS SDK をインストールします(最新のバージョンを利用してください)。
pod 'CardIO'
iOSアプリでカメラを利用する場合、なぜ必要なのかをユーザーに明示する必要があります。 Info.plist
に NSCameraUsageDescription というキーで説明を追加します。

これでカードフォームに番号読み取りのカメラアイコンが表示されます。
カード保有者名の入力を非表示にする
カード保有者名は @IBInspectable
で定義している isHolderRequired
をstoryboardから設定、またはコード上から値をセットすることで表示/非表示の切り替えができます(デフォルトは表示)。
コード上からセットする場合は、CardFormStylable#setCardHolderRequired
を利用してください。
見た目を変更する
カードフォームの種類はUITableViewに適した CardFormTableStyledView
、ラベル付きの CardFormLabelStyledView
、カード表示ありの CardFormDisplayStyledView
の3種類があります。
共通のインターフェースを持っているため、基本的な機能は同じになります。
フォームをカスタマイズするには CardFormStylable#apply
を利用してください。FormStyle
クラスで UIColor
を設定することで各コンポーネントの色を変更することができます。
let style = FormStyle(
labelTextColor: .black,
inputTextColor: .black,
tintColor: .blue,
inputFieldBackgroundColor: .white)
cardFormView.apply(style: style)
コンポーネント | FormStyle |
---|---|
Label 文字の色 | labelTextColor |
TextField 入力文字の色 | inputTextColor |
TextField エラーメッセージの色 | errorTextColor |
TextField カーソルの色 | tintColor |
TextField 背景の色 | inputFieldBackgroundColor |
Button 背景の色 | submitButtonColor |
Highlight ハイライトの色 | highlightColor |
3. カードフォームを利用せずトークン化する場合
トークンを生成するには以下のカード情報が必要になります。
- クレジットカード番号(例:
4242424242424242
) - CVC(例:
123
) - 有効期限(月)(例:
02
) - 有効期限(年)(例:
2020
) - (任意)カード保有者名(例:
TARO YAMADA
)
APIClient#createToken
は上記のカード情報を引数に呼び出すことで Result<Token, APIError>
を返し、生成したトークンまたはエラーを取得できます。
エラー項目については Error を参照ください。
payjpClient.createToken(
with: "4242424242424242",
cvc: "123",
expirationMonth: "02",
expirationYear: "2020",
name: "TARO YAMADA") { result in
switch result {
case .success(let token):
print("token_id is \(token.identifier)")
case .failure(let error):
print(error.localizedDescription)
}
}
生成済みのトークン情報を取得する
トークンIDを利用してすでに生成済みのトークンには、APIClient#getToken
を呼び出します。
payjpClient.getToken(with: tokenId) { result in
switch result {
case .success(let token):
print("token_id is \(token.identifier)")
case .failure(let error):
print(error.localizedDescription)
}
}
トークン情報を利用する
生成したトークンは支払いを始め、各種処理に利用することができます。
トークンオブジェクト(PAYJP.Token
)には例として下記のような情報が含まれます。
private extension Token {
func print() {
print("id=\(identifier)")
print("card.id=\(card.identifier)")
print("card.last4=\(card.last4Number)")
print("card.exp=\(card.expirationMonth)/\(card.expirationYear)")
print("card.name=\(card.name ?? "nil")")
}
// id=tok_5ca06b51685e001723a2c3b4aeb4
// card.id=car_e3ccd4e0959f45e7c75bacc4be90
// card.last4=4242
// card.exp=2/2020
// card.name=TARO YAMADA
}
詳細なトークンオブジェクト(PAYJP.Token
)については APIリファレンスを御覧ください。
Error
トークンのリクエストが失敗するとき、Result
に格納される Error
として APIError
と LocalError
があります。
これらのエラーは errorCode
に応じて下記のように分類されます。
Error | errorCode | 説明 |
---|---|---|
.invalidApplePayToken |
0 | ApplePayのトークン不正エラー。userInfoにキー PAYErrorInvalidApplePayTokenObject で対象のトークンが格納されています。 |
.systemError |
1 | URLSessionに由来するエラー。userInfoにキー PAYErrorSystemErrorObject で元のエラーが格納されています。 |
.invalidResponse |
2 | レスポンスの不正エラー。userInfoにキー PAYErrorInvalidResponseObject でHTTPレスポンスが格納されています。 |
.serviceError |
3 | サーバーエラー。userInfoにキー PAYErrorServiceErrorObject でエラーレスポンスが格納されています。エラーレスポンスの type と code を確認し、APIリファレンスの Error を参照ください。 |
.invalidJSON |
4 | JSONデータの不正エラー。userInfoにキー PAYErrorInvalidJSONObject でJSONデータ、PAYErrorInvalidJSONErrorObject でエラーが格納されています。 |
.invalidFormInput |
5 | 入力値不正エラー。カードフォームの入力値に問題がある場合にこのエラーが返されます。 |
.requiredThreeDSecure |
6 | 3Dセキュア認証の要求エラー。userInfoにキー PAYErrorRequiredThreeDSecureIdObject で3Dセキュアトークンが格納されています。 |
.rateLimitExceeded |
7 | レートリミット超過エラー。混雑のため、一時的に利用が制限されています。時間を空けて再度お試し下さい。 等時間をあけた再試行を提案してください。 |
Token作成リクエストのバーストを抑制する
カード情報入力フォームがユーザーの連打などで意図せず連続でTokenの作成をリクエストできてしまうと、レートリミットの制限に達する可能性があります。
SDKが提供するカードフォーム画面はあらかじめ対策がなされていますが、カードフォーム画面を利用する以外の方法でアプリに組み込む場合、 PAYTokenOperationStatus
を利用してユーザーによる連打などで意図せずTokenの作成を連続リクエストしないよう抑制できます。
ステータスの更新は NotificationCenter
経由で通知されます。
PAYTokenOperationStatus
が .acceptable
のときにTokenを作成できるようにUIを更新してください。
override func viewDidLoad() {
// ...
NotificationCenter.default.addObserver(self,
selector: #selector(handleTokenOperationStatusChange(notification:)),
name: .payjpTokenOperationStatusChanged,
object: nil)
}
@objc private func handleTokenOperationStatusChange(notification: Notification) {
if let value = notification.userInfo?[PAYNotificationKey.newTokenOperationStatus] as? Int,
let newStatus = TokenOperationStatus.init(rawValue: value) {
self.toggleButton(enabled: newStatus == .acceptable)
}
}
ソースコード・サンプルコード
SDKはgithubで公開されています。 SDKのソースコードのほか、Swift/Objective-Cのサンプルコードをご覧になれます。