モバイルアプリケーションで支払いを受け取る方法:1つのSDKでのトークン化、NFC、光学スキャン、およびその他のバン

Android SDKの例については、フレームとWebViewに限定することなく、モバイルアプリケーションに銀行カードの支払いを受け入れるネイティブ形式を埋め込み、同時にPCI DSS監査を受けないようにする方法について既に説明しました。 それ以来、SDKは大幅に拡張され、AndroidおよびiOSの通常の形式のカード入力に次の機能が追加されました。

-AndroidおよびiOS用のReact Nativeライブラリ

-マップの詳細を使用したレイアウトレイアウトフォームのカスタマイズ

-光学スキャンカード機能

-NFCテクノロジーを使用したAndroidでの非接触型支払いの受け入れ

この出版物では、モバイルアプリケーションでの支払いで一般的にできること、ライフハックと落とし穴について説明し、最後にデモアプリケーションコードの例を挙げて、私のスマートフォンのNFCリーダーを使用して友人からカードの借金を償却する方法を説明します。





ケース1.ワンクリックで通常の借方または支払いのために、クライアントのカードをバックエンドに添付します。



バックエンドがPCI DSSに従って認証されていない場合、カード番号とその有効期限をデータベースに保存できないことを理解することが重要です。 したがって、カード識別子をクライアントのアカウントにバインドする前に、最初にカードをトークン化する必要があります。 これを行うには、クライアントの参加により、できれば3D-Secureを使用して、モバイルアプリケーションで最初の支払いを行う必要があります。たとえば、1通貨単位など、カードの小額をブロックします。 この場合、3Dセキュアは、最初に、販売時点で、将来の定期的な請求に対する金銭的請求(チャージバック)から身を守るために必要です。次に、たとえば、ロシアのSberbankとPrivatbankカードを使用して、変換を改善するために必要ですウクライナでは、ほとんどの場合、3D-Secureを使用しないトランザクションは失敗します。

したがって、カードトークンを取得するには、 requiredRecTokenおよび検証パラメーターを渡す必要があります(モバイルアプリケーションの作成方法の詳細については、記事、冒頭で示したリンク、およびgithubのデモアプリケーションコードを参照してください)。

order.setRequiredRecToken(true)
      
      





 order.setVerification(true)
      
      





requiredRecTokenパラメーターは、カードの承認が成功したときにカードトークンを返却し、 検証する必要があります-資金をカードから差し引く必要はありませんが、それらをブロックして返却するだけで十分です(支払いゲートウェイは自動的に返却します)。

応答として、支払いゲートウェイはパラメーターrecToken-カードトークン、 recTokenLifeTime-トークンの有効期限(基本的にカードの有効期限)およびmaskedCard-支払い方法を選択する際にクライアントにさらに表示するためにトークンにバックエンドでリンクする必要があるマスクされたカード番号を返します。

これで、カードトークンを使用して、クライアントのリクエスト時または支払いの期日であればいつでも、サーバー間APIを介してトークン引き落とす方法を呼び出し、必要な金額を償却することができます。

落とし穴:

統計によると、カード所有者のかなりの部分は、モバイルデバイスとゲートウェイに依存しないいくつかの理由により、モバイルデバイスで3DSecureを介して支払うことができません。

-SMSが届かない可能性がある、またはユーザーがSMSアプリケーションと自分のアプリケーションを切り替えると、WebViewまたはシステムブラウザで開くときに3D-Secureパスワードフォームが失われる

-スマートフォンまたはタブレット上の3D-Secure銀行ページのレイアウトが上昇した(銀行がそのようなページを適応させることはほとんどありません)

-銀行のウェブサーバーは、安全でないTSL 1.0プロトコルのサポートを無効にしており、Androidバージョン<4.1では3D-Secureを使用できません

ライフハック:

支払いゲートウェイでは、3D-Secureオンコールを有効/無効にすることができ、クライアントがまだ支払いできない場合は、それに適応し、3D-Secureパスワードなしで支払いを試みます。

ある支払いプロバイダーのトークンをシステムに保存すると、プロバイダーがトークンの移行に同意しない限り、それらを別のプロバイダーで使用できないことも覚えておく必要があります。



ケース2。カード番号を入力するためにフォームのレイアウトをカスタマイズします。



多くの場合、カード番号、有効期限、およびcvv2を入力するためのフィールドを、SDKの標準レイアウトで提供される順序とは異なる順序で配置する必要があります。 ただし、PCI DSS要件により、カード番号入力フィールドを取得して標準のEditTextコンポーネントに置き換えることはできません。 これらの目的のために、柔軟なレイアウトを開発しました。 柔軟なレイアウトは、モバイルアプリケーションのスタイルを継承し、フォーム要素を任意のシーケンスおよびデザインで配置できると同時に、バックエンドの側にカードデータが誤って転送されるのを防ぎます。



SDKでカード入力を整理するには、2つのメカニズムがあります。

CardInputView-使用する準備ができたビュー。

CardInputLayoutは、独自のマークアップスタイルでビューを構築するための単なるレイアウトラッパーです。



基本的にCardInputView = CardInputLayout + CardNumberEdit + CardExpMmEdit + CardExpYyEdit + CardCvvEdit。

XMLのCardInputViewの単純化された構造は、次のように飲むことができます。



 <com.cloudipsp.android.CardInputLayout> <com.cloudipsp.android.CardNumberEdit/> <LinearLayout android:orientation="horizontal"> <com.cloudipsp.android.CardExpMmEdit /> <com.cloudipsp.android.CardExpYyEdit /> </LinearLayout> <com.cloudipsp.android.CardCvvEdit /> <com.cloudipsp.android.CardInputLayout>
      
      







したがって、十分な想像力がある限り、入力要素を完全に自由にカスタマイズおよび配置できます。 遵守する必要があるルールは1つだけです。各入力要素(CardNumberEdit、CardExpMmEdit、CardExpYyEdit、CardCvvEdit)はCardInputLayoutに一度だけ存在する必要があり、ビューのネストレベルは重要ではありません。

これは次のようなものです。



落とし穴:

入力フィールドをカスタマイズするときは、次のことに注意してください。

-cvv2は3文字または4文字の長さにすることができます

-カード番号は14〜19文字です

-SDKの分岐を作成し、レイアウトの実装を変更することで、デザインを最も正確にカスタマイズできます(カードの詳細をバックエンドに渡さない場合、これは禁止されていません)。 ただし、分岐すると、ゲートウェイからのSDK更新のサポートと新機能の統合が失われます。

ライフハック:

多くの場合、カードの詳細を入力するためのフォームで入力を見つけて、カード所有者の名前と姓、および郵便番号を入力することができます。 CISでの支払いの場合、99%のケースでこれを行う必要はありません。米国、カナダ、英国の一部の銀行のみがこの技術をサポートしています。これはAddress Verification Systemと呼ばれ、小切手が機能するためには、取得銀行と銀行の両方でサポートされなければなりません。発行者





ケース3.カメラとNFCを介してカードをスキャンする機能を接続します



光カードスキャン機能は、Androidではandroid-sdk-opticalライブラリで、iOSではcard.io SDKを使用するCloudipspOpticalライブラリで実装されます

NFCスキャンは、 android-sdk-nfcおよびreact-native-cloudipsp-nfcライブラリを使用して実装され、Androidでのみ使用可能です。 AppleはiOS 11+以降、サードパーティの開発者にRFIDタグを読み取る可能性を開いていますが、銀行カードからのEMVタグの読み取りはまだ利用できません。



NFCを使用するためのサンプルデモ
 package com.cloudipsp.nfcexample; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.util.Patterns; import android.view.View; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; import com.cloudipsp.android.Card; import com.cloudipsp.android.CardInputView; import com.cloudipsp.android.Cloudipsp; import com.cloudipsp.android.CloudipspWebView; import com.cloudipsp.android.Currency; import com.cloudipsp.android.Order; import com.cloudipsp.android.Receipt; import com.cloudipsp.nfc.NfcCardBridge; public class MainActivity extends Activity implements View.OnClickListener { private static final int MERCHANT_ID = 1396424; private EditText editAmount; private Spinner spinnerCcy; private EditText editEmail; private EditText editDescription; private CardInputView cardInput; private CloudipspWebView webView; private Cloudipsp cloudipsp; private NfcCardBridge nfcCardBridge; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); nfcCardBridge = new NfcCardBridge(this); findViewById(R.id.btn_amount).setOnClickListener(this); editAmount = (EditText) findViewById(R.id.edit_amount); spinnerCcy = (Spinner) findViewById(R.id.spinner_ccy); editEmail = (EditText) findViewById(R.id.edit_email); editDescription = (EditText) findViewById(R.id.edit_description); cardInput = (CardInputView) findViewById(R.id.card_input); cardInput.setHelpedNeeded(true); findViewById(R.id.btn_pay).setOnClickListener(this); webView = (CloudipspWebView) findViewById(R.id.web_view); cloudipsp = new Cloudipsp(MERCHANT_ID, webView); spinnerCcy.setAdapter(new ArrayAdapter<Currency>(this, android.R.layout.simple_spinner_item, Currency.values())); if (savedInstanceState == null) { processIntent(getIntent()); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_amount: fillTest(); break; case R.id.btn_pay: processPay(); break; } } private void fillTest() { editAmount.setText("1"); editEmail.setText("test@test.com"); editDescription.setText("test payment"); } private void processPay() { editAmount.setError(null); editEmail.setError(null); editDescription.setError(null); final int amount; try { amount = Integer.valueOf(editAmount.getText().toString()); } catch (Exception e) { editAmount.setError(getString(R.string.e_invalid_amount)); return; } final String email = editEmail.getText().toString(); final String description = editDescription.getText().toString(); if (TextUtils.isEmpty(email) || !Patterns.EMAIL_ADDRESS.matcher(email).matches()) { editEmail.setError(getString(R.string.e_invalid_email)); } else if (TextUtils.isEmpty(description)) { editDescription.setError(getString(R.string.e_invalid_description)); } else { final Currency currency = (Currency) spinnerCcy.getSelectedItem(); final Order order = new Order(amount, currency, "vb_" + System.currentTimeMillis(), description, email); order.setLang(Order.Lang.ru); final Card card; if (nfcCardBridge.hasCard()) { card = nfcCardBridge.getCard(order); cardInput.display(null); } else { card = cardInput.confirm(); } cloudipsp.pay(card, order, new Cloudipsp.PayCallback() { @Override public void onPaidProcessed(Receipt receipt) { Toast.makeText(MainActivity.this, "Paid " + receipt.status.name() + "\nPaymentId:" + receipt.paymentId, Toast.LENGTH_LONG).show(); } @Override public void onPaidFailure(Cloudipsp.Exception e) { if (e instanceof Cloudipsp.Exception.Failure) { Cloudipsp.Exception.Failure f = (Cloudipsp.Exception.Failure) e; Toast.makeText(MainActivity.this, "Failure\nErrorCode: " + f.errorCode + "\nMessage: " + f.getMessage() + "\nRequestId: " + f.requestId, Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.NetworkSecurity) { Toast.makeText(MainActivity.this, "Network security error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.ServerInternalError) { Toast.makeText(MainActivity.this, "Internal server error: " + e.getMessage(), Toast.LENGTH_LONG).show(); } else if (e instanceof Cloudipsp.Exception.NetworkAccess) { Toast.makeText(MainActivity.this, "Network error", Toast.LENGTH_LONG).show(); } else { Toast.makeText(MainActivity.this, "Payment Failed", Toast.LENGTH_LONG).show(); } e.printStackTrace(); } }); } } @Override public void onBackPressed() { if (webView.waitingForConfirm()) { webView.skipConfirm(); } else { super.onBackPressed(); } } @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); processIntent(intent); } private void processIntent(Intent intent) { if (nfcCardBridge.readCard(intent)) { Toast.makeText(this, "NFC Card read success", Toast.LENGTH_LONG).show(); nfcCardBridge.displayCard(cardInput); } } }
      
      







通常の実装とは、NfcCardBridgeが存在し、カードが読み取られるイベント(readCard)を待機するIntentがハングしている点が異なります。

落とし穴:

カードはNFCを使用して読み取られますが、存在しない通常のカードは依然としてカードの金融承認のためのプロトコルです。 すなわち この機能を完全に機能させるには、インターネットでの支払いのためにカードを開いておく必要があります。

ライフハック:

簡単なアプリケーションを作成することにより、それを使用して他人のカードから自分のカードに資金を移動し、他人のカードを携帯電話に持ち込むことができます。 たとえば、カードの借金として友人から少額を償却する必要がある場合に便利です。 一方で、実用的で便利なものになります-非常に壮観です。 カードからカードへの振替サービスを使用するには、まずFondy支払いプラットフォームのサイトで登録し、資金が送金される銀行カードを財務設定にリンクする必要があります。 セキュリティ上の理由から、3D-SecureサポートなしでNFCを介して引き落とすことができる金額は、$ 4に相当する額を超えることはできません。



All Articles