The main classes and interfaces involved here are:
SamsungPay– Class for a merchant app to get Samsung Pay SDK information and the status of Samsung Pay on the device.
SamsungPay
PaymentManager – Class to provide payment/transaction functionality.
PaymentManager
CardInfoListener – Interface for requestCardInfo result callbacks from Samsung Pay.
CardInfoListener
CustomSheetTransactionInfoListener – Interface for transaction success/failure callbacks from Samsung Pay; payment information is provided with a success callback and must be used by the merchant app for processing the payment.
CustomSheetTransactionInfoListener
The flow pictured next captures the essential online payment API process between merchant apps integrated with the Samsung Pay SDK and Samsung Pay and the merchant’s payment gateway (PG).
Reflected in the diagram above are the following operations:
Check the ready status of Samsung Pay. Start the Payment Manager to establish the service binding and verify the merchant app. Get payment card information and the payment amount, including updates. Get/update the user’s billing and shipping addresses, including an updated payment amount if shipping charges will be incurred. Authenticate the user. Submit payment information to PG. Verify transaction success or failure.
To complete the payment, the merchant’s designated payment gateway (PG) handles one of two types of tokens: gateway tokens (indirect) or network tokens (direct). The Samsung Pay SDK supports both types.
The essential difference between the two types is who decrypts the token information. Network tokens require that the merchant app handles decryption of the token bundle or work with the PG to handle decryption, whereas Gateway token decryption is handled by the PG via the Samsung-PG Interface Server.
Check with your PG to determine its specific requirements for payment processing.
Regardless of the PG model employed, direct or indirect, the goal is to offer Samsung Pay as a secure payment method within your merchant app. The most common use case involves the following general steps:
To make a purchase, the user selects to “Buy” or got to checkout after adding items to a shopping cart. Now in checkout, the user selects a payment option; for example, either the merchant’s “standard” method or Samsung Pay. Upon selecting Samsung Pay, the user is presented with a payment sheet that allows for card selection and shipping address confirmation with the option to add/modify information for this order, whereupon the user: * Makes payment card selection from the list of enrolled cards. * Chooses to change or add the delivery address. * Enters required address information in the form presented and saves it. * Authenticates the payment method, amount, and delivery with a biometric verification (fingerprint, iris…) or PIN.
Before displaying the Samsung Pay button, a partner app can query card brand information for the user’s currently enrolled payment cards in Samsung Pay to determine if payment is supported with the enrolled card. For example, if a merchant app accepts one card brand exclusively but the user has not registered any cards matching this brand in Samsung Pay, the merchant app needs to determine whether or not to display the Samsung Pay button for this purchase checkout.
To query the card brand, use the requestCardInfo() API method of the PaymentManager class. The requestFilter is optional bundle data reserved for future use. The merchant app does not need to set a value for it now. However, before calling this method, CardInfoListener must be registered so its listener can provide the following events:
requestCardInfo()
onResult
onFailure
The following snippet shows how to retrieve the list of supported card brands from Samsung Pay:
Bundle bundle = new Bundle(); bundle.putString(SamsungPay.PARTNER_SERVICE_TYPE, SamsungPay.ServiceType.INAPP_PAYMENT.toString()); PartnerInfo partnerInfo = new PartnerInfo(serviceId, bundle); paymentManager = new PaymentManager(context, partnerInfo); paymentManager.requestCardInfo(new Bundle(), cardInfoListener); // get Card Brand List //CardInfoListener is for listening requestCardInfo() callback events final PaymentManager.CardInfoListener cardInfoListener = new PaymentManager.CardInfoListener() { // This callback is received when the card information is received successfully @Override public void onResult(List<CardInfo> cardResponse) { int visaCount = 0, mcCount = 0, amexCount = 0, dsCount = 0; String brandStrings = "Card Info : "; if (cardResponse != null) { PaymentManager.Brand brand; for (int i = 0; i < cardResponse.size(); i++) { brand = cardResponse.get(i).getBrand(); switch (brand) { case AMERICANEXPRESS: amexCount++; break; case MASTERCARD: mcCount++; break; case VISA: visaCount++; break; case DISCOVER: dsCount++; break; default:// Other card brands break; } } } brandStrings += " VI=" + visaCount + ", MC=" + mcCount + ", AX=" + amexCount + ", DS=" + dsCount; Toast.makeText(context, "cardInfoListener onResult" + brandStrings, Toast.LENGTH_LONG).show(); } // This callback is received when the card information cannot be retrieved. // For example, when SDK service in the Samsung Pay app dies abnormally. @Override public void onFailure(int errorCode, Bundle errorData) { //Called when an error occurs during In-App cryptogram generation Toast.makeText(context, "cardInfoListener onFailure : " + errorCode, Toast.LENGTH_LONG).show(); } };
Upon successful initialization of the SamsungPay class, the merchant app needs to create a transaction request with payment information
As of SDK v2.0.00, the normal payment sheet is deprecated. All merchant apps must now use the custom payment sheet, which offers more dynamic controls for tailoring the UI look and feel with additional customer order and payment data.Merchant app developers choosing to temporarily continue offering the normal sheet will need to configure their Android manifest to reflect the pre-2.0.00 version of the SDK used to implement their app’s existing normal sheet, although this is not recommended. In all cases, merchant app developers should update their apps with the latest version of the SDK as soon as possible to avoid timing out using an earlier version of the SDK when responding to Samsung Pay callbacks.
To initiate a payment transaction with Samsung Pay’s custom payment sheet, your merchant app must populate the following mandatory fields in CustomSheetPaymentInfo:
Merchant Name - as it will appear in Samsung Pay’s payment sheet, as well as the user's card account statement.
Merchant Name
Amount - the constituent transaction properties (currency, item price, shipping price, tax, total price) which together determine the total amount the user is agreeing to pay the merchant.
Amount
Not populating the mandatory fields throws an IllegalArgumentException
Optionally, the following fields can be added to the payment information:
Merchant ID- can be used for the merchant’s own designated purpose at its discretion unless the merchant uses an indirect PG like Stripe or Braintree. If an indirect PG is used, this field must be set to the merchant’s Payment Gateway ID fetched from the Samsung Pay Developers portal.
Merchant ID
Order Number - usually created by the merchant app via interaction with a PG. This number is required for refunds and chargebacks. In the case of Visa cards, the value is mandatory. The allowed characters are [A-Z][a-z][0-9,-] and the length of the value can be up to 36 characters.
Order Number
Address - the user’s billing and/or shipping address (see Applying an AddressControl for details).
Address
Allowed Card Brands - specifies card brands accepted by the merchant. If no brand is specified, all brands are accepted by default. If at least one brand is specified, all other card brands not specified are set to "card not supported’ on the payment sheet.
Allowed Card Brands
Here’s the 'CustomSheetPaymentInfo' structure:
public class CustomSheetPaymentInfo implements Parcelable { private String version; private String merchantId; private String merchantName; private String orderNumber; private AddressInPaymentSheet addressInPaymentSheet = AddressInPaymentSheet.DO_NOT_SHOW; private List<SpaySdk.Brand> allowedCardBrand;e private CardInfo cardInfo; private boolean isCardHolderNameRequired = false; private boolean isRecurring = false; private String merchantCountryCode; private CustomSheet customSheet; private Bundle extraPaymentInfo; }
Your merchant app sends this CustomSheetPaymentInfo to Samsung Pay via the applicable Samsung Pay SDK API methods. Upon successful user authentication in direct mode, Samsung Pay returns the above "Payment Info" structure and a result string. The result string is forwarded to the PG by your merchant app to complete the transaction. It will vary based on the PG you’re using.
CustomSheetPaymentInfo
The following example demonstrates how to populate customSheet in the CustomSheetPaymentInfo class. See Sample merchant app using custom payment sheet below for example usage of each CustomSheet control.
Sample merchant app using custom payment sheet
/* * Make user's transaction details. * The merchant app should send CustomSheetPaymentInfo to Samsung Pay via * the applicable Samsung Pay SDK API method for the operation being invoked. */ private CustomSheetPaymentInfo makeCustomSheetPaymentInfo() { ArrayList<PaymentManager.Brand> brandList = new ArrayList<>(); // If the supported brand is not specified, all card brands in Samsung Pay are // listed in the Payment Sheet. brandList.add(PaymentManager.Brand.VISA); brandList.add(PaymentManager.Brand.MASTERCARD); brandList.add(PaymentManager.Brand.AMERICANEXPRESS); /* * Make the SheetControls you want and add them to custom sheet. * Place each control in sequence with AmountBoxControl listed last. */ CustomSheet customSheet = new CustomSheet(); customSheet.addControl(makeBillingAddressControl()); customSheet.addControl(makeShippingAddressControl()); customSheet.addControl(makePlainTextControl()); customSheet.addControl(makeShippingMethodSpinnerControl()); customSheet.addControl(makeAmountControl()); CustomSheetPaymentInfo customSheetPaymentInfo = new CustomSheetPaymentInfo.Builder() .setMerchantId("123456") .setMerchantName("Sample Merchant") // Merchant requires billing address from Samsung Pay and // sends the shipping address to Samsung Pay. // Show both billing and shipping address on the payment sheet. .setAddressInPaymentSheet(CustomSheetPaymentInfo.AddressInPaymentSheet. NEED_BILLING_SEND_SHIPPING) .setAllowedCardBrands(brandList) .setCardHolderNameEnabled(true) .setRecurringEnabled(false) .setCustomSheet(customSheet) .build(); return customSheetPaymentInfo; }
The startInAppPayWithCustomSheet() method of the PaymentManager class is applied to request payment using a custom payment sheet in Samsung Pay. The two methods are defined as follows:
startInAppPayWithCustomSheet() - initiates the payment request with a custom payment sheet. The payment sheet persist for 5 minutes after the API is called. If the time limit expires, the transaction fails.
startInAppPayWithCustomSheet()
updateSheet() - updates the custom payment sheet if any values on the sheet are changed. As of API level 1.5, a merchant app can update the custom sheet with a custom error message. Refer to Updating sheet with custom error message.
updateSheet()
When you call the startInAppPayWithCustomSheet() method, a custom payment sheet is displayed on the merchant app screen. From it, the user can select a registered card for payment and change the billing and shipping addresses, as necessary.
The result is delivered to CustomSheetTransactionInfoListener, which provides the following events:
onSuccess() - called when Samsung Pay confirms payment. It provides the CustomSheetPaymentInfo object and the paymentCredential JSON string.
onSuccess()
CustomSheetPaymentInfo is used for the current transaction. It contains amount, shippingAddress, merchantId, merchantName, orderNumber. API methods exclusively available in the onSuccess() callback comprise:
getPaymentCardLast4DPAN()
getPaymentCardLast4FPAN()
getPaymentCardBrand()
getPaymentCurrencyCode()
getPaymentShippingAddress()
getPaymentShippingMethod()
onCardInfoUpdated() - called when the user changes the payment card. In this callback, updateSheet() method must be called to update current payment sheet.
onCardInfoUpdated()
onFailure() - called when the transaction fails; returns the error code and errorData bundle for the failure
onFailure()
Here’s how to call the startInAppPayWithCustomSheet() method of the PaymentManager class:
/* * CustomSheetTransactionInfoListener is for listening callback events of In-App custom sheet payment. * This is invoked when card is changed by the user on the custom payment sheet, * and also with the success or failure of online (In-App) payment. */ private PaymentManager.CustomSheetTransactionInfoListener transactionListener = new PaymentManager.CustomSheetTransactionInfoListener() { // This callback is received when the user changes card on the custom payment sheet in Samsung Pay @Override public void onCardInfoUpdated(CardInfo selectedCardInfo, CustomSheet customSheet) { /* * Called when the user changes card in Samsung Pay. * Newly selected cardInfo is passed so merchant app can update transaction amount * based on different card (if needed), */ AmountBoxControl amountBoxControl = (AmountBoxControl)customSheet.getSheetControl(AMOUNT_CONTROL_ID); amountBoxControl.updateValue(PRODUCT_ITEM_ID, 1000); //item price amountBoxControl.updateValue(PRODUCT_TAX_ID, 50); // sales tax amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10); // Shipping fee amountBoxControl.updateValue(PRODUCT_FUEL_ID, 0, "Pending"); // additional item status amountBoxControl.setAmountTotal(1060, AmountConstants.FORMAT_TOTAL_PRICE_ONLY); // grand total customSheet.updateControl(amountBoxControl); // Call updateSheet() with AmountBoxControl; mandatory. try { paymentManager.updateSheet(customSheet); } catch (IllegalStateException | NullPointerException e) { e.printStackTrace(); } } /* * This callback is received when the payment is approved by the user and the transaction payload * is generated. Payload can be an encrypted cryptogram (network token mode) or the PG's token * reference ID (gateway token mode). */ @Override public void onSuccess(CustomSheetPaymentInfo response, String paymentCredential, Bundle extraPaymentData) { /* * Called when Samsung Pay creates the transaction cryptogram, which merchant app then sends * to merchant server or PG to complete in-app payment. */ try { String DPAN = response.getCardInfo().getCardMetaData().getString(SpaySdk.EXTRA_LAST4_DPAN, ""); String FPAN = response.getCardInfo().getCardMetaData().getString(SpaySdk.EXTRA_LAST4_FPAN, ""); Toast.makeText(context, "DPAN: " + DPAN + "FPAN: " + FPAN, Toast.LENGTH_LONG).show(); } catch (NullPointerException e) { e.printStackTrace(); } Toast.makeText(context, "Transaction : onSuccess", Toast.LENGTH_LONG).show(); } @Override public void onFailure(int errorCode, Bundle errorData) { // Called when an error occurs during cryptogram generation Toast.makeText(context, "Transaction : onFailure : "+ errorCode, Toast.LENGTH_LONG).show(); } }; private void startInAppPayWithCustomSheet() { // Show custom payment sheet try { Bundle bundle = new Bundle(); bundle.putString(SamsungPay.PARTNER_SERVICE_TYPE, SamsungPay.ServiceType.INAPP_PAYMENT.toString()); PartnerInfo partnerInfo = new PartnerInfo(serviceId, bundle); paymentManager = new PaymentManager(context, partnerInfo); // Request payment using Samsung Pay paymentManager.startInAppPayWithCustomSheet(makeCustomSheetPaymentInfo(), transactionListener); } catch (IllegalStateException | NumberFormatException | NullPointerException | IllegalArgumentException e) { e.printStackTrace(); } }
When an address is provided by Samsung Pay, onAddressUpdated()is called whenever address information is updated in the custom payment sheet. You can use the updateSheet() method to update the shipping fee or any other relevant information in the payment sheet. Set the errorCode to determine if the address provided by Samsung Pay app is invalid, out of delivery, or does not exist. For example, when the:
onAddressUpdated()
For all such cases, the merchant app should call updateSheet() with one of the following error codes:
The sample code included below under Applying the Address Control demonstrates how to use the updateSheet() method for 'addressControl' in the payment sheet.
The paymentCredential is the resulting output of the startInAppPayWithCustomSheet() method. The structure varies depending on the PG you’re using and the integration model—direct or indirect). The following paymentCredential is for a Visa card.
Sample paymentCredential JSON output (using JWE-only)
{ "billing_address":{"city":"BillingCity","country":"USA","state_province":"CA","street":"BillingAddr1","zip_postal_code":"123456"}, "card_last4digits":"1122", "3DS":{"data":"eyJhbGciOiJSU0ExXzUiLCJraWQiOiJCak91a1h2aFV4WU5wOFIwVGs2Y25OaCtZWWFqZXhIeHRVZ0VFdHlhYy9NPSIsInR5cCI6IkpPU0UiLCJjaGFubmVsU2VjdXJpdHlDb250ZXh0IjoiUlNBX1BLSSIsImVuYyI6IkExMjhHQ00ifQ.Fg2OOUvHdGKkIVyBa2S5KtUrPWUeujKZEyxz7n6kALhQahszv3P5JaBaOJ-RoKcznFjDg3qierzjktU7zXST9gwv4Oclahpfdw64w0X6TtAxeYJiIVkJUG-edXXTWaJeyeIkgC68wEhF1CltSqG4zLWi6upVCAywdPpBN0Hl0C5WcF5Az4WABYtV_Fda5aHGuyPnE70kEQRTWdlacW9MzEJx2Xth7Msd9OHoulR8LUQ-7gha17jHoOBwgMoQ9q0hAoCNm0LjWiuhKoRyyu-Njulnbkk8FZus_AIuMgdv2YN9ygFqIlMculb0VWuF0YeKX6IsgAxi0ZQhLiUsJkCZ_w.AuZZxoG46lnrtk3Q.QE2llwS30VzH-ZduuE8b045CnfRm2p-RjZGBnZcHELS3v26N64cFg1AV5mtP5f-fSwbJ3ntP5x4V1NK8FmdY0uSPxzeMfvl5badGAC7w9FrXt6X5xV1Fqu6-q-ZkbxcB9bYgownt983BcKOE1bd5djxFBOdLrc4j68ikDjc5M3LEBDx6hV0aQzKmilCH-JeVL3AwQyKBny4Vj7m3Fizw7u1PRLI2ZfWUkXDfS4Vwv3bPm4QUDEMVnHXJ.qTYmdmn4ne93juljNmWkJg","type":"S","version":"100"}, "merchant_ref":"MerchantId", "method":"3DS", "recurring_payment":false }
Decrypt using the merchant’s private key. Below is sample private key.
-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA4LZYjQR+dqd/XLEOXct9jwTJXHD2PTJke9djtMIjKi0h2Oc2GHoW4uJHHY/1jvFt2+zCnjTOXuVLp+76/DWA3bCwFRj+fPP6x5KKYlPb+dJDYo1TTumltNqCWymJB3u7jBC+xR4vKfRzqjxkE7xhN/SBb82uE8c3sMzVKYnUJi<…> -----END RSA PRIVATE KEY-----
The decrypted output will be similar to this:
{ "amount":"1000", "currency_code":"USD", "utc":"1490266732173", "eci_indicator":"5", "tokenPAN":"1234567890123456", "tokenPanExpiration":"0420", "cryptogram":"AK+zkbPMCORcABCD3AGRAoACFA==" }
Depending on the structure of the payment processing API provided by your PG, your merchant app can send either of these directly to the PG: + Entire paymentCredential output + Extracted “3DS” part only
Consult your PG documentation for specific guidance.
In indirect (gateway token) mode, paymentCredential is the PG’s token reference ID and its status. Here’s a sample of the JSON output.
{ "reference":"tok_18rje5E6SzUi23f2mEFAkeP7", "status":"AUTHORIZED" }
For Stripe, your merchant app should be able to pass this token object directly to Charge or another appropriate payment processing API provided by the PG.