SDK Programming Guide
  • 4.1.2 Sample merchant app using custom payment sheet
  • Extending the normal type of payment sheet, a custom payment sheet offers the following additional: controls:
  • Applying the AddressControl
  • This control is used for displaying the billing address or shipping address on a custom payment sheet based on My info menu in the Samsung Pay app.

    When allocating the control, controlId and SheetItemType are needed to distinguish billing address from shipping address. The merchant app can set the following properties with AddressControl.

    • Address title – displays a custom title on the payment sheet. If empty, the default title (e.g., “Billing address”) is displayed
    • Address – provides various methods to return address detailes.
    • As of API level 1.3, addressee’s phoneNumber is added for custom payment sheet. Merchant app can retrieve the phone number using getPhoneNumber() method of CustomSheetPaymentInfo.Address.

      As of API level 1.5, addressee’s email address is added. Merchant app can retrieve the email address using getEmail() method. Also Merchant app can set display option for shipping address on the payment sheet with setDisplayOption() method. For more information, refer to the Samsung Pay SDK API reference and sample code in Samsung Pay SDK.

    • SheetUpdatedListener – used to capture the response from the Samsung Pay app; merchant app must deliver to Samsung Pay app an AmountBoxControl for display on a custom payment sheet using onResult(). Within this callback, updateSheet() method must be called to update current payment sheet.
    • ErrorCode – used for containing error codes directly related to the address.
  • Figures 5 and 6 show the workflows for the BillingAddressControl and ShippingAddressControl, respectively.
  • Figure 5: BillingAddressConrol flow

  • Figure 6: ShippingAddressConrol flow

  • The sample code that follows demonstrates the use of AddressControl in a custom sheet.
  • // For billing address
    private AddressControl makeBillingAddressControl() {
        AddressControl billingAddressControl = new AddressControl(BILLING_ADDRESS_ID, SheetItemType.BILLING_ADDRESS);
        billingAddressControl.setAddressTitle("Billing Address [control]");
    
        //This callback is received when Controls are updated.
        billingAddressControl.setSheetUpdatedListener(new SheetUpdatedListener() {
            @Override
            public void onResult(String updatedControlId, CustomSheet customSheet) {
                Log.d(TAG,"onResult billingAddressControl updatedControlId: " + updatedControlId);
    
                // Validate billing address and set errorCode if needed.
                AddressControl addressControl = (AddressControl)customSheet.getSheetControl(updatedControlId);
                CustomSheetPaymentInfo.Address billAddress = addressControl.getAddress();
                int errorCode = validateBillingAddress(billAddress);
                Log.d(TAG, "onResult updateSheetBilling  errorCode: " + errorCode);
                addressControl.setErrorCode(errorCode);
                customSheet.updateControl(addressControl);
    
                AmountBoxControl amountBoxControl = (AmountBoxControl) customSheet.getSheetControl(AMOUNT_CONTROL_ID);
                amountBoxControl.updateValue(PRODUCT_ITEM_ID, 1000);
                amountBoxControl.updateValue(PRODUCT_TAX_ID, 50);
                amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10);
                amountBoxControl.updateValue(PRODUCT_FUEL_ID, 0, "Pending");
                amountBoxControl.setAmountTotal(1060, AmountConstants.FORMAT_TOTAL_PRICE_ONLY);
                customSheet.updateControl(amountBoxControl);
                // Call updateSheet with AmountBoxControl. This is mandatory.
                try { 
    					paymentManager.updateSheet(customSheet);
                     } catch (IllegalStateException | NullPointerException e) {
    					e.printStackTrace();
                     }
    
                }
        });
        return billingAddressControl;
    }
    
    // For Shipping address
    private AddressControl makeShippingAddressControl() {
        AddressControl shippingAddressControl = new AddressControl(SHIPPING_ADDRESS_ID, SheetItemType.SHIPPING_ADDRESS);
        shippingAddressControl.setAddressTitle("Shipping Address [control]");
        CustomSheetPaymentInfo.Address shippingAddress = new CustomSheetPaymentInfo.Address.Builder()
    		.setAddressee("name")
    		.setAddressLine1("addLine1")
    		.setAddressLine2("addLine2")
    		.setCity("city")
    		.setState("state")
    		.setCountryCode("USA")
    		.setPostalCode("zip")
    		.setPhoneNumber("123-1234-1234")
    		.setEmail("12345@samsung.com")
    		.build();
        shippingAddressControl.setAddress(shippingAddress);
        /*
         *      Set address display option on custom payment sheet.
         * If displayOption is not set, then default addressControl is displayed on custom payment sheet.
         *      The possible values are combination of below constants:
         *      {DISPLAY_OPTION_ADDRESSEE}
         *      {DISPLAY_OPTION_ADDRESS}
         *      {DISPLAY_OPTION_PHONE_NUMBER}
         *      {DISPLAY_OPTION_EMAIL}
         */
    
        int displayOption_val = AddressConstants.DISPLAY_OPTION_ADDRESSEE;  
        // Addressee is mandatory
        displayOption_val += AddressConstants.DISPLAY_OPTION_ADDRESS;
        displayOption_val += AddressConstants.DISPLAY_OPTION_PHONE_NUMBER;
        displayOption_val += AddressConstants.DISPLAY_OPTION_EMAIL;
        shippingAddressControl.setDisplayOption(displayOption_val);
        return shippingAddressControl;
    }
    
  • Here’s how these controls display on a custom payment sheet:
  • Applying the PlainTextControl
  • This control is used for displaying a title with a two lines of text or a single line of text without a title. When allocating this control, a controlId is needed. The merchant app sets both the title, as applicable, and the text. Figure 7 shows the flow between apps.
  • Figure 7: PlainTextConrol flow

  • The merchant app code invoking this class will look something like the following:
  • private PlainTextControl makePlainTextControl() {
        PlainTextControl plainTextControl = new PlainTextControl("ExamplePlainTextControlId");
        plainTextControl.setText("Plain Text [example]", "This is example of PlainTextControl");
        return plainTextControl;
    }
    
  • And this is how it will display on the custom payment sheet.
  • Applying the AmountBoxControl
  • This control is used for displaying purchase amount information on a custom payment sheet, when allocated, AmountBoxControl requires a controlId and a currencyCode and consists of Item(s) and AmountTotal, defined as follows and diagrammed in Figure 8:
    • Item – consists of id, title, price, and extraPrice. If there is extraPrice in AmountBoxControl, its text is displayed on the custom payment sheet even though there is an actual (numerical) price value. If there is no extraPrice, currencyCode with price value is displayed.
    • AmountTotal – consists of price and displayOption. The displayOption allows predefined strings only. The merchant app can set the text to “Estimated amount”, “Amount pending”, “Pending”, “Free”, and so forth. The UI format for the string is different for each option.
    • Note
    • The setAmountTotal API may accept strings that are not predefined as an argument but, on execution, will generate an invalid parameter condition or return an error code.
  • For details, see the Samsung Pay SDK API Reference, available in the Documentation folder of your downloaded SDK package.
  • Figure 8: AmountBoxControl flow

  • The following example demonstrates how to use AmountBoxControl in a custom sheet.
  • private AmountBoxControl makeAmountControl() {
        AmountBoxControl amountBoxControl = new AmountBoxControl(AMOUNT_CONTROL_ID, "USD");
        amountBoxControl.addItem(PRODUCT_ITEM_ID, "Items", 1000, "");
        amountBoxControl.addItem(PRODUCT_TAX_ID, "Tax", 50, "");
        amountBoxControl.addItem(PRODUCT_SHIPPING_ID, "Shipping", 10, "");
        amountBoxControl.setAmountTotal(1060, AmountConstants.FORMAT_TOTAL_PRICE_ONLY);
        amountBoxControl.addItem(3, PRODUCT_FUEL_ID, "FUEL", 0, "Pending");
        return amountBoxControl;
    }
    
  • The merchant app can also add new items using the addItem() method of AmountControlBox during callback.
    • Important
    • The merchant app must call the updateValue() method of AmountBoxControl for each amount item, even if the amount has not changed and remains the same as the original value.
  • The custom payment sheet will display like this:
  • When the custom sheet is updated, the merchant can add new items to AmountBoxControl. For example, if user selects a specific card in the custom payment sheet for which the merchant offers a discount, item can be added via the updateSheet() API.
  • // Example for adding new item while updating values
    AmountBoxControl amount = (AmountBoxControl) sheet.getSheetControll("id_amount");
    
    amount.updateValue("itemId", 900);
    amount.updateValue("taxId", 50);
    amount.updateValue("shippingId", 10);
    amount.updateValue("fuelId", 0);
    // Add “Dicount” item
    amount.addItem(4, "discountId", "Discount", -60, "");
    
    amount.setAmountTotal(1000, AmountConstants.FORMAT_TOTAL_PRICE_ONLY);
    sheet.updateControl(amount);
    // Call updateSheet with AmountBoxControl. This is mandatory.
    try {
    		paymentManager.updateSheet(sheet);
    		} catch (IllegalStateException | NullPointerException e) {
    			e.printStackTrace();
    		}
    
  • Applying the SpinnerControl
  • This control is used for displaying Spinner options on a custom payment sheet.

    When allocating the control, controlId, title, and SheetItemType are needed to distinguish between the types of spinner displayed. The merchant app can set the following properties with SpinnerControl.

    • title – displays a custom Spinner title on the payment sheet.
    • SheetItemType – provides various types of Spinner.
  • As of API level 1.6, SHIPPING_METHOD_SPINNER and INSTALLMENT_SPINNER are available.
    • Note
    • SHIPPING_METHOD_SPINNER can be used when the shipping address comes from Samsung Pay app; i.e., when the CustomSheetPaymentInfo.AddressInPaymentSheet option is set to NEED_BILLING_AND_SHIPPING or NEED_SHIPPING_SPAY.
  • The merchant app code invoking this class will look something like the following:
  • // Construct SpinnerControl
    SpinnerControl spinnerControl = new SpinnerControl(SHIPPINGMETHOD_SPINNER_ID, "Shipping Method ", 
    SheetItemType.SHIPPING_METHOD_SPINNER);
    // User can select one of the shipping method option on the payment sheet.
    spinnerControl.addItem("shipping_method1", getString(R.string.standard_shipping_free));
    spinnerControl.addItem("shipping_method2", getString(R.string.twoday_shipping) );
    spinnerControl.addItem("shipping_method3", getString(R.string.oneday_shipping));
    spinnerControl.setSelectedItemId("shipping_method1"); // Set default option
    
    // This listener is for listening SheetControl events
    spinnerControl.setSheetUpdatedListener(new SheetUpdatedListener() {
        @Override
        public void onResult(String updatedControlId, CustomSheet customSheet) {
            AmountBoxControl amountBoxControl = (AmountBoxControl) customSheet.getSheetControl(AMOUNT_CONTROL_ID);
            SpinnerControl spinnerControl = (SpinnerControl) customSheet.getSheetControl(updatedControlId);
            switch (spinnerControl.getSelectedItemId()) {
                case "shipping_method1":
                    amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10);
                    break;
                case "shipping_method2":
                    amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10 + 0.1);
                    break;
                 case "shipping_method3":
                      amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10 + 0.2);
                      break;
                default:
                    amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10);
                    break;
            }
            amountBoxControl.setAmountTotal(1000 + amountBoxControl.getValue(PRODUCT_SHIPPING_ID), 
    		AmountConstants.FORMAT_TOTAL_PRICE_ONLY);
            customSheet.updateControl(amountBoxControl);
    
            // Call updateSheet with AmountBoxControl. This is mandatory.
            try {
                paymentManager.updateSheet(customSheet);
            } catch (IllegalStateException | NullPointerException e) {
                e.printStackTrace();
            }
        }
    });
    
    
    // Construct SpinnerControl
    SpinnerControl spinnerControl = new SpinnerControl(INSTALLMENT_SPINNER_ID, "Installment", SheetItemType.INSTALLMENT_SPINNER);
    spinnerControl.addItem("installment1", "1 month without interest");
    spinnerControl.addItem("installment2", "2 months with 2% monthly interest");
    spinnerControl.addItem("installment3", "3 months with 2.2% monthly interest");
    spinnerControl.setSelectedItemId("installment1"); // Set default option
    
    // This listener is for listening SheetControl events
    spinnerControl.setSheetUpdatedListener(new SheetUpdatedListener() {
        @Override
        public void onResult(String updatedControlId, CustomSheet customSheet) {
            AmountBoxControl amountBoxControl = (AmountBoxControl) customSheet.getSheetControl(AMOUNT_CONTROL_ID);
            SpinnerControl spinnerControl = (SpinnerControl) customSheet.getSheetControl(updatedControlId);
            double TotalInterest = 0;
            switch (spinnerControl.getSelectedItemId()) {
                case "installment1":
                    amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, TotalInterest);
                    break;
                case "installment2":
                    // Calculate total interest again and updateValue
                    amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, TotalInterest);
                    break;
                case "installment3":
                    // Calculate total interest again and updateValue
                    amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, TotalInterest);
                    break;
                default:
                    amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, TotalInterest);
                    break;
            }
            amountBoxControl.setAmountTotal(1000 + amountBoxControl.getValue(PRODUCT_TOTAL_INTEREST_ID), AmountConstants.FORMAT_TOTAL_PRICE_ONLY);
            customSheet.updateControl(amountBoxControl);
    
            // Call updateSheet with AmountBoxControl. This is mandatory.
            try {
                paymentManager.updateSheet(customSheet);
            } catch (IllegalStateException | NullPointerException e) {
                e.printStackTrace();
            }
         }
    });
    
  • For details, see the Samsung Pay SDK API Reference, available in the Documentation folder of your downloaded SDK package.
  • Updating sheet with custom error message
  • As of API level 1.5, new updateSheet() API method has been introduced for custom error message display on the custom payment sheet.
  • void updateSheet(final CustomSheet sheet, final int errorCode,
            final String customErrorMessage)
    
  • This API method is an extended version of the existing updateSheet(sheet) API method, in which the merchant has the ability to display the partner app’s custom error message on the custom payment sheet’s authentication area.
    • Note
    • Pre-defined error messages in earlier versions of the SDK (pre-1.5) are deprecated.
  • updateSheet(sheet) can be used to inform the user of any error scenarios via a custom message on the custom payment sheet.
  • //In any abnormal cases, merchant can update sheet with CUSTOM_MESSSAGE error code.
    paymentManager.updateSheet(customSheet, PaymentManager.CUSTOM_MESSAGE,
     "Phone number is not valid. Please change your phone number!");