Web Checkout Integration
The Samsung Pay Web Checkout feature can be easily implemented on your website.
Prerequisites
Before you can integrate Samsung Pay Web Checkout, the following requirements must be met:
-
You have a Samsung Pay merchant ID. To obtain it, complete the partner onboarding process.
-
The acquirer and issuer support tokenized transactions for in-app purchases, according to card network specifications.
Integrating Samsung Pay Web Checkout
To integrate the Samsung Pay Web Checkout solution to your website:
-
Include the Samsung Pay Web SDK JavaScript file in your website front-end:
<script src="https://img.mpay.samsung.com/gsmpi/sdk/samsungpay_web_sdk.js"></script>
-
Define the supported payment methods and Samsung Pay API version in the
paymentMethods
object.
You must also provide your unique merchant ID in theserviceId
key.const paymentMethods = { "version": "2", "serviceId": "dcc1cbb25d6a470bb42926", "protocol": "PROTOCOL_3DS", "allowedBrands": ["visa","mastercard"] }
-
Initialize the Samsung Pay client by creating an instance of the
PaymentClient
object.
You must also define the operation environment for your Web Checkout:STAGE
= staging environment, used for testingPRODUCTION
= production environment, for actual payments
const samsungPayClient = new SamsungPay.PaymentClient({environment: "STAGE"});
-
Check whether Samsung Pay is supported for the payment request, using the
isReadyToPay()
method with thepaymentMethods
object:samsungPayClient.isReadyToPay(paymentMethods).then(function(response) { if (response.result) { // add a payment button } }).catch(function(err) { console.error(err); });
-
Create a container for the Samsung Pay button:
<div id="samsungpay-container"></div>
-
Implement the Samsung Pay button.
When the user clicks the button, theonClick()
listener is triggered.function createAndAddButton() { const samsungPayButton = samsungPayClient.createButton({ onClick: onSamsungPayButtonClicked, buttonStyle: "black", type: "buy" }); document.getElementById("samsungpay-container").appendChild(samsungPayButton); }
-
Create the transaction information.
ThetransactionDetail
object contains the order number, merchant information, and total amount for the purchase.const transactionDetail = { "orderNumber": "DSTRF345789dsgTY", "merchant": { "name": "Virtual Shop", "url": "virtualshop.com", "id": "xn7qfnd", "countryCode": "US" }, "amount": { "option": "FORMAT_TOTAL_ESTIMATED_AMOUNT", "currency": "USD", "total": 300 } }
-
Launch the payment sheet.
When theonClick()
event is triggered, your event handler must call theloadPaymentSheet()
method, which initiates the Web Checkout UI flow. When the user confirms the payment from their mobile device, you receive thepaymentCredential
object generated by the device. -
Extract the payment credential information from the
3DS.Data
key within thepaymentCredential
object and process it through your payment provider. -
Inform the Samsung server of the payment result using the
notify()
method within thepaymentResult
object.
samsungPayClient.loadPaymentSheet(paymentMethods, transactionDetail).then(function(paymentCredential) {
// Process payment with provider
...
...
const paymentResult = {
"status": "CHARGED",
"provider": "PG Name"
}
samsungPayClient.notify(paymentResult);
}).catch(error => {
// Show error in developer console for debugging
console.error(err);
});
PaymentMethods
, TransactionDetail
, and PaymentCredential
data structures, see the API Reference.Decrypting Payment Credentials
For security reasons, the payment credential data that you receive is protected by JSON Web Encryption (JWE). To decrypt the payment credentials:
-
Generate a DER file from your private key:
$ openssl pkcs8 -topk8 -in Merchant.key -outform DER -nocrypt -out rsapriv.der
-
Decrypt the JWE encrypted data.
-
Sample implementation in Java:
import java.io.*; import java.security.*; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import javax.crypto.*; ... public static void main(String[] args) { String encPayload = {{encryptedPayload}}; String privateKeyFilePath = "./rsapriv.der"; getDecryptedData(encPayload, privateKeyFilePath); } public static String getDecryptedData(String encPayload, String privateKeyFilePath) { String delims = "[.]"; String[] tokens = encPayload.split(delims); Decoder urlDecoder = Base64.getUrlDecoder(); byte[] encKey = urlDecoder.decode(tokens[1]); byte[] iv = urlDecoder.decode(tokens[2]); byte[] cipherText = urlDecoder.decode(tokens[3]); byte[] tag = urlDecoder.decode(tokens[4]); byte[] plainText = new byte[cipherText.length]; try { // Read private key file File privateKeyFile = new File(privateKeyFilePath); DataInputStream dis = new DataInputStream(new FileInputStream(privateKeyFile)); byte[] privKeyBytes = new byte[(int) privateKeyFile.length()]; dis.read(privKeyBytes); dis.close(); // Set private key spec PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(privKeyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); PrivateKey privKey = keyFactory.generatePrivate(spec); Cipher decryptCipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); decryptCipher.init(Cipher.DECRYPT_MODE, privKey); byte[] plainEncKey = decryptCipher.doFinal(encKey); final Cipher aes128Cipher = Cipher.getInstance("AES/GCM/NoPadding"); final GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(16 * Byte.SIZE, iv); final SecretKeySpec keySpec = new SecretKeySpec(plainEncKey, "AES"); aes128Cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); int offset = aes128Cipher.update(cipherText, 0, cipherText.length, plainText, 0); aes128Cipher.update(tag, 0, tag.length, plainText, offset); aes128Cipher.doFinal(plainText, offset); } catch (Exception e) { } return new String(plainText); }
-
Sample implementation in C#
The following packages for C# .netCore are required:Install-Package ChilkatDnCore -Version 9.5.0.69 (https://www.nuget.org/packages/ChilkatDnCore/) Install-Package jose-jwt (https://www.nuget.org/packages/jose-jwt/)
using Jose; using System; using System.Text; ... static void Main(string[] args) { String encPayload = {{encryptedPayload}}; String privateKeyFilePath = "./rsapriv.der"; getDecryptedData(encPayload, privateKeyFilePath); } static void getDecryptedData(String encPayload, String privateKeyFilePath) { char delims = '.'; string[] tokens = encPayload.Split(delims); byte[] header = Base64Url.Decode(tokens[0]); string headerString = Encoding.UTF8.GetString(header); Console.WriteLine("headerString: " + headerString); byte[] encKey = Base64Url.Decode(tokens[1]); byte[] iv = Base64Url.Decode(tokens[2]); byte[] cipherText = Base64Url.Decode(tokens[3]); byte[] tag = Base64Url.Decode(tokens[4]); byte[] plainText = new byte[cipherText.Length]; Chilkat.Rsa rsa = new Chilkat.Rsa(); bool success = rsa.UnlockComponent("Anything for 30-day trial"); if (success != true) { Console.WriteLine(rsa.LastErrorText); return; } Chilkat.PrivateKey privateKey = new Chilkat.PrivateKey(); success = privateKey.LoadPemFile(privateKeyFilePath); if (success != true) { Console.WriteLine(privateKey.LastErrorText); return; } success = rsa.ImportPrivateKeyObj(privateKey); if (success != true) { Console.WriteLine(rsa.LastErrorText); return; } rsa.EncodingMode = "base64"; rsa.OaepPadding = false; bool usePrivateKey = true; byte[] plainEncKey = rsa.DecryptBytes(encKey, usePrivateKey); Console.WriteLine( "plainEncKey: " + Encoding.UTF8.GetString(plainEncKey) + ", size: " + plainEncKey.Length); AesGcmEncryption aesGcmEngine = new AesGcmEncryption(128); plainText = aesGcmEngine.Decrypt(null, plainEncKey, iv, cipherText, tag); Console.WriteLine("plainText: " + Encoding.UTF8.GetString(plainText)); }
-