Sample apps, use cases, and UX strategies are included here to aid you in understanding the SDK and implementing it in your application. Sample source code and APKs can be downloaded from Download section.
Sample merchant app
Included with the Samsung Pay SDK to demonstrate its features, the sample merchant app shows you how to implement the payment sheet’s dynamic controls to leverage additional customer order and payment data and/or create a more custom UI look and feel.
The following payment sheet controls are available:
AddressControl
PlainTextControl
AmountBoxControl
SpinnerControl
Controls are applied to suit a particular purpose or need. For example, displaying a promotion notice in the payment sheet using the PlainTextControl.
Applying an AddressControl
This control is used to display the billing or shipping address on the payment sheet based on Samsung Pay’s My info user profile or addresses provided by your merchant app during the transaction request.
When creating the control, controlId and SheetItemType are needed to distinguish the billing address from the shipping address. Otherwise, your merchant app sets the following properties:
Address title – displays a merchant-defined title on the payment sheet. If empty, the default title (such as “Billing address”) is displayed.
Address – provides various methods to retrieve address details.
The merchant app can retrieve the phone number using the 'getPhoneNumber'() method of 'CustomSheetPaymentInfo'.Address. Starting from API level 1.5, the addressee’s email address has also been added. Retrieve the email address using 'getEmail'(). You can also set a display option for the shipping address with 'setDisplayOption'(). For more information, see the Samsung Pay SDK-API reference Javadoc and the sample code included with the Samsung Pay SDK.
SheetUpdatedListener – used to capture the response from the Samsung Pay app; merchant app must deliver to the Samsung Pay app an AmountBoxControl to display payment information on a custom payment sheet. When the onResult() callback is called, the updateSheet() method must also be called to update the current payment sheet.
ErrorCode – used for containing error codes directly related to the address.
The workflows for BillingAddressControl and ShippingAddressControl are shown below.
The following sample code demonstrates use of AddressControl on the payment sheet.
fun makeBillingAddressControl(): AddressControl {
val billingAddressControl = if (!isZipCodeOnly) {
// For billing address
AddressControl(BILLING_ADDRESS_ID, SheetItemType.BILLING_ADDRESS)
billingAddressControl.addressTitle = "Billing Address"
} else {
/*
* For billing address with zip code only.
* Since API level 2.19, SheetItemType.ZIP_ONLY_ADDRESS
* For US country only
*/
AddressControl(BILLING_ADDRESS_ID, SheetItemType.ZIP_ONLY_ADDRESS)
billingAddressControl.addressTitle = "Zip Code"
}
//This callback is received when Controls are updated
billingAddressControl.sheetUpdatedListener = sheetUpdatedListener()
return billingAddressControl
}
//Listener for billing or zip code only billing address
fun sheetUpdatedListener(): SheetUpdatedListener {
return SheetUpdatedListener { updatedControlId: String, customSheet: CustomSheet ->
Log.d(
TAG,
"onResult billingAddressControl updatedControlId: $updatedControlId"
)
val addressControl =
customSheet.getSheetControl(updatedControlId) as AddressControl
val billAddress = addressControl.address
//Validate only zipcode or billing address. And set errorCode if needed.
if (addressControl.sheetItem.sheetItemType == SheetItemType.ZIP_ONLY_ADDRESS) {
val errorCode: Int = validateZipcodeBillingAddress(billAddress)
Log.d(TAG, "onResult updateSheetBilling errorCode: $errorCode")
addressControl.errorCode = errorCode
customSheet.updateControl(addressControl)
} else {
val errorCode = validateBillingAddress(billAddress)
Log.d(TAG, "onResult updateSheetBilling errorCode: $errorCode")
addressControl.errorCode = errorCode
customSheet.updateControl(addressControl)
}
// update transaction values
val amountBoxControl =
CustomSheet.getSheetControl(AMOUNT_CONTROL_ID) as AmountBoxControl
amountBoxControl.updateValue(PRODUCT_ITEM_ID, 1000.0)
amountBoxControl.updateValue(PRODUCT_TAX_ID, 50.0)
amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10.0)
amountBoxControl.updateValue(PRODUCT_FUEL_ID, 0.0, "Pending")
amountBoxControl.setAmountTotal(1060.0, AmountConstants.FORMAT_TOTAL_PRICE_ONLY)
customSheet.updateControl(amountBoxControl)
try {
// Call updateSheet for the full AmountBoxControl; mandatory
paymentManager.updateSheet(customSheet)
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: NullPointerException) {
e.printStackTrace()
}
}
}
// For Shipping address
fun makeShippingAddressControl(): AddressControl {
val shippingAddressControl = AddressControl(SHIPPING_ADDRESS_ID, SheetItemType.SHIPPING_ADDRESS)
shippingAddressControl.addressTitle = "Shipping Address"
val shippingAddress = CustomSheetPaymentInfo.Address.Builde()
.setAddressee("name")
.setAddressLine1("addLine1")
.setAddressLine2("addLine2")
.setCity("city")
.setState("state")
.setCountryCode("USA")
.setPostalCode("zip")
.setPhoneNumber("555-123-1234")
.setEmail("user@samsung.com")
.build()
shippingAddressControl.address = 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}
*/
var 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.displayOption = displayOption_val
return shippingAddressControl
}
Here’s how these controls display on a custom payment sheet:
Applying a PlainTextControl
This control is used for displaying a title with a two lines of text or a single line of text without a title on the payment sheet. When allocating this control, a controlId is needed. The merchant app sets both the title, as applicable, and the text. Diagrammed below is the flow between your merchant app and Samsung Pay.
The merchant app code invoking this class would look something like the following:
fun makePlainTextControl(): PlainTextControl {
val plainTextControl = PlainTextControl("ExamplePlainTextControlId")
plainTextControl.setText("Plain Text [example]", "This is example of PlainTextControl")
return plainTextControl
}
And this is how it displays on the custom payment sheet.
Applying an AmountBoxControl
AmountBoxControl is used for displaying purchase amount information on the payment sheet. It requires a controlId and a currencyCode, and consists of Item(s) and AmountTotal, defined as follows and diagrammed on the next page:
Item – consists of id, title, price, and extraPrice. If there is an extraPrice in AmountBoxControl, its text is displayed on the payment sheet even though there is an actual (numerical) price value. If there is no extraPrice, then currencyCode with the price value is displayed.
AmountTotal – consists of price and displayOption. The displayOption allows predefined strings only. Your 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.
NoteThe setAmountTotal API may accept strings that are not predefined as an argument, but itgenerates an invalid parameter condition or returns an error code in such cases.
For details, see the Javadoc Samsung Pay SDK-API Reference, available in the Documentation folder of your downloaded SDK package.
Here’s a coding example to demonstrate the use of AmountBoxControl in a payment sheet.
The merchant app can also add new items using the 'addItem'() method of 'AmountControlBox' during callback.
ImportantYour merchant app needs to call the 'updateValue'(item_id) method of 'AmountBoxControl** to update each amount item. Then call 'CustomSheet'.'updateControl'() to make the changes take effect in 'CustomSheet'. Eventually, 'PaymentManager'.'updateSheet'('CustomSheet') must be called to let Samsung Pay know that no further action is pending in the merchant app.
When the custom sheet is updated, the merchant can add new items to AmountBoxControl. For example, if the user selects a specific card in the payment sheet which the merchant offers, a discount item can be added via the updateSheet().
// Example for adding new item while updating values
val amount = sheet.getSheetControll("id_amount")
amount.updateValue("itemId", 900.0)
amount.updateValue("taxId", 50.0)
amount.updateValue("shippingId", 10.0)
amount.updateValue("fuelId", 0.0)
// Add “Discount” item
amount.addItem(4, "discountId", "Discount", -60.0, "")
amount.setAmountTotal(1000.0, AmountConstants.FORMAT_TOTAL_PRICE_ONLY)
sheet.updateControl(amount)
// Call updateSheet with AmountBoxControl; mandatory
try {
paymentManager.updateSheet(sheet)
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: NullPointerException) {
e.printStackTrace()
}
Applying the SpinnerControl
This control is used for displaying Spinner options on a payment sheet. When creating the control, controlId, title, and SheetItemType are needed to distinguish between the types of spinner to be displayed. Your merchant app sets the following properties with SpinnerControl.
title – the merchant-defined Spinner title to appear the payment sheet.
SheetItemType – provides various types of Spinner.
A SHIPPING_METHOD_SPINNER and an INSTALLMENT_SPINNER are the two types of spinner available as of API level 1.6.
NoteSHIPPING_METHOD_SPINNER can be used when the shipping address comes from the Samsung Pay app; i.e., when the CustomSheetPaymentInfo.AddressInPaymentSheet option is set to NEED_BILLING_AND_SHIPPING or NEED_ SHIPPING_SPAY. When the shipping address is provided by the merchant app (SEND_SHIPPING or NEED_BILLING_ SEND_SHIPPING), it is not changeable in the payment sheet. The shipping fee (if applied) must be pre-calculated on the merchant app side.
Here’s an example of constructing a SpinnerControl within your merchant app:
// Construct SpinnerControl for shipping method
val spinnerControl = SpinnerControl(
SHIPPINGMETHOD_SPINNER_ID, "Shipping Method ",
SheetItemType.SHIPPING_METHOD_SPINNER
)
// Let the user can select one shipping method option on the payment sheet
spinnerControl.addItem(
"shipping_method_1",
getString(android.R.string.standard_shipping_free)
)
spinnerControl.addItem("shipping_method_2", getString(android.R.string.twoday_shipping))
spinnerControl.addItem("shipping_method_3", getString(android.R.string.oneday_shipping))
spinnerControl.selectedItemId = "shipping_method_1" // Set default option
// Listen for SheetControl events
spinnerControl.setSheetUpdatedListener(SheetUpdatedListener { updatedControlId, customSheet ->
val amountBoxControl =
CustomSheet.getSheetControl(AMOUNT_CONTROL_ID) as AmountBoxControl
val spinnerControl = CustomSheet.getSheetControl(updatedControlId) as SpinnerControl
when (spinnerControl.selectedItemId) {
"shipping_method_1" -> amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10.0)
"shipping_method_2" -> amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10 + 0.1)
"shipping_method_3" -> amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10 + 0.2)
else -> amountBoxControl.updateValue(PRODUCT_SHIPPING_ID, 10.0)
}
amountBoxControl.setAmountTotal(
1000 + amountBoxControl.getValue(PRODUCT_SHIPPING_ID),
AmountConstants.FORMAT_TOTAL_PRICE_ONLY
)
customSheet.updateControl(amountBoxControl)
// Call updateSheet with AmountBoxControl; mandatory.
try {
paymentManager.updateSheet(customSheet)
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: NullPointerException) {
e.printStackTrace()
}
})
// Construct SpinnerControl for installment plan
val spinnerControl = SpinnerControl(
INSTALLMENT_SPINNER_ID, "Installment",
SheetItemType.INSTALLMENT_SPINNER
)
spinnerControl.addItem("installment_1", "1 month without interest")
spinnerControl.addItem("installment_2", "2 months with 2% monthly interest")
spinnerControl.addItem("installment_3", "3 months with 2.2% monthly interest")
spinnerControl.selectedItemId = "installment_1" // Set default option
// Listen for SheetControl events
spinnerControl.setSheetUpdatedListener(SheetUpdatedListener { updatedControlId, customSheet ->
val amountBoxControl: AmountBoxControl =
CustomSheet.getSheetControl(AMOUNT_CONTROL_ID) as AmountBoxControl
val spinnerControl = CustomSheet.getSheetControl(updatedControlId) as SpinnerControl
val totalInterest = 0.0
when (spinnerControl.selectedItemId) {
"installment1" -> amountBoxControl.updateValue(
PRODUCT_TOTAL_INTEREST_ID,
totalInterest
)
"installment2" -> // Calculate total interest again and updateValue
amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, totalInterest)
"installment3" -> // Calculate total interest again and updateValue
amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, totalInterest)
else -> amountBoxControl.updateValue(PRODUCT_TOTAL_INTEREST_ID, totalInterest)
}
amountBoxControl.setAmountTotal(
1000 + amountBoxControl.getValue(PRODUCT_TOTAL_INTEREST_ID),
AmountConstants.FORMAT_TOTAL_PRICE_ONLY
)
customSheet.updateControl(amountBoxControl)
// Call updateSheet with AmountBoxControl; mandatory.
try {
paymentManager.updateSheet(customSheet)
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: NullPointerException) {
e.printStackTrace()
}
})
Update sheet with custom error message
To display a custom error message on the payment sheet, use updateSheet with customErrorMessage.
fun updateSheet(sheet : CustomSheet, errorCode : Int, customErrorMessage : String)
This API method is an extended version of the existing updateSheet(sheet) method which gives the merchant the ability to display a custom error message in the payment sheet’s authentication area. It can be used to inform the user of any foreseen error scenarios encountered.
// Update sheet with CUSTOM_MESSSAGE error code
paymentManager.updateSheet(customSheet, PaymentManager.CUSTOM_MESSAGE,"Phone number entered is not valid. Please change your phone number.")
Sample issuer app
The Samsung Pay SDK also provides a sample issuer app to showcase Samsung Pay SDK features.
For more information, refer to the Javadoc Samsung Pay SDK API reference and sample code.
Manage Your Cookies
We use cookies to improve your experience on our website and to show you relevant
advertising. Manage you settings for our cookies below.
Essential Cookies
These cookies are essential as they enable you to move around the website. This
category cannot be disabled.
Company
Domain
Samsung Electronics
.samsungdeveloperconference.com
Analytical/Performance Cookies
These cookies collect information about how you use our website. for example which
pages you visit most often. All information these cookies collect is used to improve
how the website works.
Company
Domain
LinkedIn
.linkedin.com
Meta (formerly Facebook)
.samsungdeveloperconference.com
Google Inc.
.samsungdeveloperconference.com
Functionality Cookies
These cookies allow our website to remember choices you make (such as your user name, language or the region your are in) and
tailor the website to provide enhanced features and content for you.
Company
Domain
LinkedIn
.ads.linkedin.com, .linkedin.com
Advertising Cookies
These cookies gather information about your browser habits. They remember that
you've visited our website and share this information with other organizations such
as advertisers.
Company
Domain
LinkedIn
.linkedin.com
Meta (formerly Facebook)
.samsungdeveloperconference.com
Google Inc.
.samsungdeveloperconference.com
Preferences Submitted
You have successfully updated your cookie preferences.