Define the supported payment methods and Samsung Pay API version in the paymentMethods object.
You must also provide your unique merchant ID in the serviceId key.
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 testing
STAGE_WITHOUT_APK = staging environment, used to simulate the testing process without using a device authorization. Visit Staging without APK full guide here.
PRODUCTION = production environment, for actual payments
const samsungPayClient = new SamsungPay.PaymentClient({environment: "STAGE"});
If your project has a Content-Security-Policy (CSP) applied, please ensure that you add a nonce to the CSS to maintain compliance. This can be done by updating your SDK configuration as follows:
const samsungPayClient = new SamsungPay.PaymentClient({environment: "STAGE", nonce: "your-nonce"});
Check whether Samsung Pay is supported for the payment request, using the isReadyToPay() method with the paymentMethods object:
samsungPayClient.isReadyToPay(paymentMethods).then(function(response) {
if (response.result) {
// add a payment button
}
}).catch(function(err) {
console.error(err);
});
Add the Samsung Pay button to your page using the official button asset:
<div id="samsungpay-container">
<button id="samsung-pay-btn">
<img src="/your/path../samsung-pay-button.png" alt="Samsung Pay" style="{follow the Samsung's official branding guideline}" />
</button>
</div>
NoteDownload the official Samsung Pay button image and branding guideline from Download page and use it directly in your HTML as shown here. Download.
Launch the payment sheet.
When the onClick() event is triggered, your event handler must call the loadPaymentSheet() method, which initiates the Web Checkout UI flow. When the user confirms the payment from their mobile device, you receive the paymentCredential object generated by the device.
Extract the payment credential information from the 3DS.Data key within the paymentCredential object and process it through your payment provider.
Inform the Samsung server of the payment result using the notify() method within the paymentResult 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);
});
Payment credential sample
The paymentCredential is the resulting output of the loadPaymentSheet() method.
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DeveloperPortalSample {
public static void main(String[] args) throws Exception {
// Example JWE string (replace with your actual JWE and private key path)
String encryptedText = {{encryptedPayload}};
String privateKeyPath = "./rsapriv.der";
String PRIVATE_KEY = Base64.getEncoder().encodeToString(Files.readAllBytes(Paths.get(privateKeyPath)));
String result = decryptJWE(encryptedText, PRIVATE_KEY);
System.out.println(result);
}
public static String decryptJWE(String encryptedText, String privateKeyText) throws Exception {
// Split JWE parts by '.'
String delims = "[.]";
String[] tokens = encryptedText.split(delims);
if (tokens.length < 5) {
throw new IllegalArgumentException("Invalid JWE format");
}
// Decode and parse JWE header
byte[] headerBytes = Base64.getUrlDecoder().decode(tokens[0]);
String headerJson = new String(headerBytes);
ObjectMapper mapper = new ObjectMapper();
JsonNode header = mapper.readTree(headerJson);
// Extract algorithm information from header
String alg = header.has("alg") ? header.get("alg").asText() : "RSA1_5";
String enc = header.has("enc") ? header.get("enc").asText() : "A128GCM";
// Convert private key
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyText);
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
// Decode encrypted key, IV, ciphertext, and authentication tag
byte[] encKey = Base64.getUrlDecoder().decode(tokens[1]);
byte[] iv = Base64.getUrlDecoder().decode(tokens[2]);
byte[] cipherText = Base64.getUrlDecoder().decode(tokens[3]);
byte[] tag = Base64.getUrlDecoder().decode(tokens[4]);
// Create Cipher instance based on key management algorithm
String keyManagementAlgorithm;
boolean useAAD = false;
if ("RSA-OAEP".equals(alg)) {
keyManagementAlgorithm = "RSA/ECB/OAEPPadding";
// At Samsung, OAEP uses AAD (Additional Authenticated Data)
useAAD = true;
} else if ("RSA1_5".equals(alg)) {
keyManagementAlgorithm = "RSA/ECB/PKCS1Padding";
// while RSA1_5 does not use AAD.
useAAD = false;
} else {
throw new IllegalArgumentException("Unsupported key management algorithm: " + alg);
}
// Decrypt the CEK (Content Encryption Key)
Cipher decryptCipher = Cipher.getInstance(keyManagementAlgorithm);
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plainEncKey = decryptCipher.doFinal(encKey);
// Create Cipher instance based on content encryption algorithm
String contentEncryptionAlgorithm;
int gcmTagLength;
if ("A128GCM".equals(enc) || "A256GCM".equals(enc)) {
contentEncryptionAlgorithm = "AES/GCM/NoPadding";
gcmTagLength = 128;
} else {
throw new IllegalArgumentException("Unsupported content encryption algorithm: " + enc);
}
// Decrypt the content
Cipher contentCipher = Cipher.getInstance(contentEncryptionAlgorithm);
GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(gcmTagLength, iv);
SecretKeySpec keySpec = new SecretKeySpec(plainEncKey, "AES");
contentCipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec);
// AAD handling: Use Base64Url-encoded header bytes as AAD
if (useAAD) {
byte[] encodedHeader = Base64.getUrlEncoder().withoutPadding().encode(headerBytes);
contentCipher.updateAAD(encodedHeader);
}
// Concatenate cipherText and tag, then pass to doFinal
byte[] cipherData = new byte[cipherText.length + tag.length];
System.arraycopy(cipherText, 0, cipherData, 0, cipherText.length);
System.arraycopy(tag, 0, cipherData, cipherText.length, tag.length);
byte[] plainText = contentCipher.doFinal(cipherData);
return new String(plainText, java.nio.charset.StandardCharsets.UTF_8);
}
Sample implementation in C#
using System;
using System.IO;
using System.Text;
using System.Text.Json.Nodes;
using System.Security.Cryptography;
public static void Main(string[] args)
{
// Example JWE string (replace with your actual JWE and private key path)
string encryptedText = {{encryptedPayload}};
string privateKeyPath = ./rsapriv.der";
// Read the private key file (DER format)
byte[] privateKeyBytes = File.ReadAllBytes(privateKeyPath);
// Decrypt the JWE
string result = decryptJWE(encryptedText, privateKeyBytes);
// Print the result
Console.WriteLine(result);
}
public static string decryptJWE(string encryptedText, byte[] privateKeyBytes)
{
// Split JWE parts by '.'
var parts = encryptedText.Split('.');
if (parts.Length < 5)
throw new ArgumentException("Invalid JWE format");
// Decode and parse JWE header
var headerBytes = Base64UrlDecode(parts[0]);
var headerJson = Encoding.UTF8.GetString(headerBytes);
var header = JsonNode.Parse(headerJson);
// Extract algorithm information from header
string alg = header?["alg"]?.ToString() ?? "RSA1_5";
string enc = header?["enc"]?.ToString() ?? "A128GCM";
// Convert private key (assume PKCS8 DER)
using var rsa = RSA.Create();
rsa.ImportPkcs8PrivateKey(privateKeyBytes, out _);
// Decode encrypted key, IV, ciphertext, and authentication tag
var encKey = Base64UrlDecode(parts[1]);
var iv = Base64UrlDecode(parts[2]);
var cipherText = Base64UrlDecode(parts[3]);
var tag = Base64UrlDecode(parts[4]);
// Create Cipher instance based on key management algorithm
bool useAAD = false;
if (alg == "RSA-OAEP")
{
// At Samsung, OAEP uses AAD (Additional Authenticated Data)
useAAD = true;
}
else if (alg == "RSA1_5")
{
// while RSA1_5 does not use AAD.
useAAD = false;
}
else
{
throw new ArgumentException($"Unsupported key management algorithm: {alg}");
}
// Decrypt the CEK (Content Encryption Key)
byte[] plainEncKey = alg == "RSA-OAEP"
? rsa.Decrypt(encKey, RSAEncryptionPadding.OaepSHA1)
: rsa.Decrypt(encKey, RSAEncryptionPadding.Pkcs1);
// Decrypt the content
using var aes = new AesGcm(plainEncKey, 16);
var plainText = new byte[cipherText.Length];
if (useAAD)
{
// AAD handling: Use Base64Url-encoded header bytes as AAD
var encodedHeader = Encoding.ASCII.GetBytes(Base64UrlEncode(headerBytes));
aes.Decrypt(iv, cipherText, tag, plainText, encodedHeader);
}
else
{
aes.Decrypt(iv, cipherText, tag, plainText);
}
return Encoding.UTF8.GetString(plainText).TrimEnd('\0');
}
private static byte[] Base64UrlDecode(string input)
{
string s = input.Replace('-', '+').Replace('_', '/');
switch (s.Length % 4)
{
case 2: s += "=="; break;
case 3: s += "="; break;
}
return Convert.FromBase64String(s);
}
private static string Base64UrlEncode(byte[] input)
{
return Convert.ToBase64String(input)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
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.