API Specification

App2App SDK Integration Specs

Description & Use

Rp SDK is an App2App SDK for Samsung Wallet driver's license service online scenarios.
This SDK provides an implementation for direct communication between the Samsung Wallet and Partner applications.

Build the settings

RpSdk requires additional dependencies with:

dependencies {
    ...
    implementation("rp-sdk-1.0-release.aar")
    implementation("androidx.core:core-ktx:1.3.2")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
    implementation("androidx.lifecycle:lifecycle-livedata-core-ktx:2.7.0")
    implementation("io.reactivex.rxjava2:rxjava:2.2.21")
    implementation("io.reactivex.rxjava2:rxkotlin:2.4.0")
    implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
    implementation("com.squareup.okhttp3:okhttp:4.11.0")
    implementation("com.google.code.gson:gson:2.10.1")
    implementation("org.bouncycastle:bcprov-jdk15to18:1.66")
    implementation("com.nimbusds:nimbus-jose-jwt:9.37.3")
    ...
}

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    ...
    <uses-permission android:name="android.permission.INTERNET" />
     <queries>
        <package android:name="com.samsung.android.spay" />
    </queries>
    ...
</manifest>

R8 / Proguard

The specific rules are already bundled into the AAR which can be interpreted by R8 automatically

SDK Method

App2App SDK supports one method.

  • request()

Signature & parameters of the request method.

fun request(targetPackageName: String, requestId: String, appLink: String, onResponseListener: OnResponseListener? = null)

Parameter name

Description

targetPackageName

The Pakcage name to connect to.

requestId

A Random string to identify each request

appLink

The appLink built by Samsung MCS server guide

onResponseListener

A listener to receive each events or requests

Sample code

binding.button.setOnClickListener {
    rpClientApis.request("com.samsung.android.spay", UUID.randomUUID().toString(), appLink, object : RpClientApis.OnResponseListener {
        override fun onGetMdocRequestData(deviceEngagementBytes: ByteArray): ByteArray? {
            Log.i(TAG, "onGetMdocRequestData($deviceEngagementBytes)")
            /**
            * 1. prepare mDoc request data (ISO-18013-5)
            * 2. build sessionEstablishmentBytes (ISO-18013-5)
            * 3. Encrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
            **/
            return "encryptedSessionEstablishmentBytes"
        }
         override fun onMdocResponse(encryptedResponse: ByteArray) {
            Log.i(TAG, "onMdocResponse($encryptedResponse)")
            /**
            * 1. Decrypt it with HKDF (ISO-18013-5, 9.1.1.5 Cryptographic operations)
            * 2. CBOR decode it
            **/
        }
         override fun onMdocResponseFailed(exception: Exception) {
            Log.i(TAG, "onMdocResponseFailed($exception)")
        }
    })
}

Error Code Explanation

The below exceptions might occur through the onMdocResponseFailed callback

Exceptions name

Description

RpCommunicationException

This error occurs when the data requested by the listener is incorrect.

RpConnectionFailedException

This occurs when the App 2 App communication between apps is not working.
This usually occurs when the target Package Name is written incorrectly



Web2App API Integration Specs

The API specifications that need to be implemented by the RP partner are described below:

Called by Samsung to the RP partner

Send Key

Send the Wallet application key info and return the data field types requested to the client for authentication of the mDL

[Request]

Type

Value

Description

Method

POST

URL

{Partner server URL}/rp/v1.0/{cardId}/{RefId}/key

Headers

Authorization

String (1024)

Required

Credential token.
The token can have the prefix "Bearer" as an authorization type, e.g., Bearer <credentials>.
* Refer to Authorization Token for more details.

Path Parameters

cardId

String (32)

Required

Wallet card identifier.
* Refer to "Add to Wallet" Interfaces for more details.

refId

String (32)

Required

Unique content identifier defined by the content provider

Query Parameter

N/A

Payload

data

String(3000)

Required

JWT data encrypted with the public key information and card type.
If decrypted this data is decoded, and it has the following format information.
{
“data”: “XXXXXXXXXXX”,
“card”: {
"type": "relyingparty",
"subType": "others",
"designType": "us-01"
}
}

Example

POST {Partner server URL}/rp/v1.0/{cardId}/{RefId}/key
Content-Type: application/json
{
“data”: “eyJjdHkiOiJBVVRIIiwidmVyIjoiMiIsInBhcnRuZXJJZCI6InRlc3QiLCJ1dGMiOjE3MTYyMDYzNjAxMTAsImFsZyI6IlJTMjU2In0.ZXlKbGJtTWlPaUpCTVRJNFIwTk5JaXdpWVd4bklqb2lVbE5CTFU5QlJWQXRNalUySW4wLlZ5aFAxS0FnMVJHbzBDN2NIX2pYdGtfODdQbnhrRmpfWkpPcnNSUUs4MnN0OWVxTjEyVzVMOEJaX1d5NGVzMzE3VDNad0pncmpwZWdZOEk3aVlCWWRlOGJ5LXFiMjBLU3RUc3JsSzlPSlFnN1FaM2xZaUxscXlTb0VlbERvd0FPaTRMRy1JUkZWdVlrbXRiNTg3UTd1ZWNuQ1lWWGZWalVEcG01YXBFbDV3SzM1UGZ3d0dkREM2TmowZ1AwbTZ3Nk1kdl9mdDBvZWc2MWZjaGdBYnY0emxMZjU2cVYzM0t6ZjdjbWVpbkJRNnpMSGUtYmFWYXhVZk5Ld2htZWVjUzFTV3laRm1NVlJ6MEFsMnBxa0dQLVJkT1Iza3VzaVo0VjFIdy1aQ2IyVWVwYVdZRU9nUEdrVW1mbTFuOWJWT1ZmZ1NUV1F0SE5pVTFJYVRHTG1DWlpVQS5PMzZrd1g4WmJnQ21wd3o2Ll9KZEhFVXNnbm13b1drdDRMcU4xMUNCaUNTSnUtbWpYV2ZrckxoS0ZVenBsS085ckdXbUdPZ0pqUkF1NTFsOTRYc2VIVWdfWU9NS2RGR1VOMWJhMHB3Y0tFNGtJMEt2dkFOWHprODN0azBjQzROT2F6VzlmOVNTT0RhMU9IMEFoaVFzQzdDeVFqNndNLWFlVk8waEJwSEJkMEdURUh1Z3Exc21vVmxRbjBlSnJqWHM4X3FwcnpLekwtaDFPcFk1aEs1ZUg5Q3NiSms0aEhCNGNmWUlKRUJFZ09BcGZxcGFuMGFSVGFmODhhdXlqSGZHdGRMa0tLWDV0Q0RTajIxSE5TT0FhWTJVWlZrR0hxU0wzNGJabTU5aEZMNVdHa0lJcE9BMHlWUE9tQzNWTFlKV2JsMm85LkFoeDBVYTVGeTZudkxKVXVkeTAzSHc.E07YYl7IoR3885VYKsS5_q1IcpX750uU2Ge5suJSedx3Dr_U0x4tSe9_0NxM46dyWnFUXrUAGfjDnjHIBc707Li9VI3XtyiHwnwEiFydgv1QB9oddkYyZuahXQmJHVUqNcdt6DF2CAqzF5QgMVqfMGSE_t7IPU8vQFXE34DO-sKzj8ftdusS2EcdANBqOKCHih3m39NouBPFhcX68PlPcW50diXlupxwEGniU2t3Co24YlIaKLGac669aCcXDQr34utVUqhTJt_FTXkahAlzoA34_Hj_s82FivIXh1ITD74UOjZSe7IBWya_kVysoZavnmzTz2tH9CbwyCvx8wA”
}

[Response]

Type

Value

Description

HTTP Status code

200 OK

Payload

data

String(3000)

Required

JWT data encrypted with the data field types requested to the client for authentication of the mDL.

[Result]

HTTP status code

Description

200 OK

Success

400 Bad Request

Requests cannot or will not be processed due to something that is perceived to be a client error.

401 Unauthorized

Authorization token is invalid or expired.

500 Internal Server Error

The server encountered an unexpected condition that prevented it from fulfilling the request.

503 Service Unavailable

The server is not ready to handle the request.

Send authentication data

The data is encrypted according to the requested data and then transmitted along with the data card information.

[Request]

Type

Value

Description

Method

POST

URL

{Partner server URL}/rp/v1.0/{cardId}/{RefId}/auth

Headers

Authorization

String (1024)

Required

Credential token.
The token can have the prefix "Bearer" as an authorization type, e.g., Bearer <credentials>.
* Refer to Authorization Token for more details.

PathParameters

cardId

String (32)

Required

Wallet card identifier.
* Refer to "Add to Wallet" Interfaces for more details.

refId

String (32)

Required

Unique content identifier defined by the content provider

QueryParameter

N/A

Payload

data

String(3000)

Required

JWT data encrypted with the public key information and card type.
If decrypted this data is decoded, it has the following format information.
{
“data”: “XXXXXXXXXXX”,
“card”: {
"type": "idcard",
"subType": "drivers",
"designType": "us-01"
}
}

Example

POST {Partner server URL}/rp/v1.0/{cardId}/{RefId}/auth
Content-Type: application/json
{
“data”: “eyJjdHkiOiJBVVRIIiwidmVyIjoiMiIsInBhcnRuZXJJZCI6InRlc3QiLCJ1dGMiOjE3MTYyMDYzNjAxMTAsImFsZyI6IlJTMjU2In0.ZXlKbGJtTWlPaUpCTVRJNFIwTk5JaXdpWVd4bklqb2lVbE5CTFU5QlJWQXRNalUySW4wLlZ5aFAxS0FnMVJHbzBDN2NIX2pYdGtfODdQbnhrRmpfWkpPcnNSUUs4MnN0OWVxTjEyVzVMOEJaX1d5NGVzMzE3VDNad0pncmpwZWdZOEk3aVlCWWRlOGJ5LXFiMjBLU3RUc3JsSzlPSlFnN1FaM2xZaUxscXlTb0VlbERvd0FPaTRMRy1JUkZWdVlrbXRiNTg3UTd1ZWNuQ1lWWGZWalVEcG01YXBFbDV3SzM1UGZ3d0dkREM2TmowZ1AwbTZ3Nk1kdl9mdDBvZWc2MWZjaGdBYnY0emxMZjU2cVYzM0t6ZjdjbWVpbkJRNnpMSGUtYmFWYXhVZk5Ld2htZWVjUzFTV3laRm1NVlJ6MEFsMnBxa0dQLVJkT1Iza3VzaVo0VjFIdy1aQ2IyVWVwYVdZRU9nUEdrVW1mbTFuOWJWT1ZmZ1NUV1F0SE5pVTFJYVRHTG1DWlpVQS5PMzZrd1g4WmJnQ21wd3o2Ll9KZEhFVXNnbm13b1drdDRMcU4xMUNCaUNTSnUtbWpYV2ZrckxoS0ZVenBsS085ckdXbUdPZ0pqUkF1NTFsOTRYc2VIVWdfWU9NS2RGR1VOMWJhMHB3Y0tFNGtJMEt2dkFOWHprODN0azBjQzROT2F6VzlmOVNTT0RhMU9IMEFoaVFzQzdDeVFqNndNLWFlVk8waEJwSEJkMEdURUh1Z3Exc21vVmxRbjBlSnJqWHM4X3FwcnpLekwtaDFPcFk1aEs1ZUg5Q3NiSms0aEhCNGNmWUlKRUJFZ09BcGZxcGFuMGFSVGFmODhhdXlqSGZHdGRMa0tLWDV0Q0RTajIxSE5TT0FhWTJVWlZrR0hxU0wzNGJabTU5aEZMNVdHa0lJcE9BMHlWUE9tQzNWTFlKV2JsMm85LkFoeDBVYTVGeTZudkxKVXVkeTAzSHc.E07YYl7IoR3885VYKsS5_q1IcpX750uU2Ge5suJSedx3Dr_U0x4tSe9_0NxM46dyWnFUXrUAGfjDnjHIBc707Li9VI3XtyiHwnwEiFydgv1QB9oddkYyZuahXQmJHVUqNcdt6DF2CAqzF5QgMVqfMGSE_t7IPU8vQFXE34DO-sKzj8ftdusS2EcdANBqOKCHih3m39NouBPFhcX68PlPcW50diXlupxwEGniU2t3Co24YlIaKLGac669aCcXDQr34utVUqhTJt_FTXkahAlzoA34_Hj_s82FivIXh1ITD74UOjZSe7IBWya_kVysoZavnmzTz2tH9CbwyCvx8wA”
}

[Response]

Type

Value

Description

HTTP Status code

200 OK
400 Bad Request

[Result]

HTTP status code

Description

200 OK

Success

400 Bad Request

Requests cannot or will not be processed due to something
that is perceived to be a client error.

401 Unauthorized

Authorization token is invalid or expired.

500 Internal Server Error

The server encountered an unexpected condition that prevented it from fulfilling the request.

503 Service Unavailable

The server is not ready to handle the request.

Code explanation based on the sample code

JWT (JWS + JWE) decryption between the Wallet Backed Server and partner server

1. Verify by generateing a JWS using the body data

// Generate JWS by the body data
private static SignedJWT parseJWT(final String data)  {
    try {
    return SignedJWT.parse(data);
} catch (ParseException e) {
    log.error("parserJWT error class : {}, error message : {}", e.getClass(), e.getMessage());
    throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "parserJWT error");
    }
}
// Verify JWS using Samsung Public key
public RequestBody getRequestBody(final KeyRing keyRing) {
    final SignedJWT signedJWT = JWTUtils.verify(keyRing.getTargetPublicKey(), encryptedData, 60 * 10000); // Verify and generate JWS
    try {
    final String strBody = JWTUtils.getDecryptedPayloadFrom(keyRing.getSourcePrivateKey(), JWEObject.parse(signedJWT.getPayload().toString())); // Decryption JWE by the JWS
    return objectMapper.readValue(strBody, RequestBody.class); // Convert to data format requested by client
    } catch (ParseException | JsonProcessingException e) {
        log.error("getRequestBody : {}", e.getMessage());
        throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "data body parse error");
    }
}

2. Decrypt the JWE using the JWS

JWEObject.parse(signedJWT.getPayload().toString())

public static String getDecryptedPayloadFrom(final Key privateKey, final JWEObject data) {
    try {
        data.decrypt(new RSADecrypter((PrivateKey) privateKey));  // Decryption JWE using Partner Private Key
        return data.getPayload().toString();
    } catch (JOSEException e) {
        log.error("JOSEException message : {}", e.getMessage());
        throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "getDecryptedPayloadFrom error");
    }
}

3. Convert to the format send by the client

public RequestBody getRequestBody(final KeyRing keyRing) {
    final SignedJWT signedJWT = JWTUtils.verify(keyRing.getTargetPublicKey(), encryptedData, 60 * 10000); // Verify and generate JWS
    try {
        final String strBody = JWTUtils.getDecryptedPayloadFrom(keyRing.getSourcePrivateKey(), JWEObject.parse(signedJWT.getPayload().toString())); // Decryption JWE by the JWS
        return objectMapper.readValue(strBody, RequestBody.class); // Convert to data format requested by client
    } catch (ParseException | JsonProcessingException e) {
        log.error("getRequestBody : {}", e.getMessage());
        throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "data body parse error");
    }
}

Generate MdocEstablishment

1. Generate RSA Key per refId

public class TransactionContext {
    private final KeyPair keyPair;   // RSA Key
    private final byte[] clientEngagement; // Body data received through key API, base64URL decoded value

    @EqualsAndHashCode.Exclude
    private int encryptMessageCounter = 0;  // Count value when encrypted
    @EqualsAndHashCode.Exclude
    private int decryptMessageCounter = 0; // Count value when decrypted
}

private Cache<String, TransactionContext> contextCache; // RSA key management per refid with memory cache

// Generate and store RSA Key per refId only once upon first request
public TransactionContext setTransactionContext(final String key, final String base64EncodedClientEngagement) {
    log.info("base64EncodedClientPublicKey : {}", base64EncodedClientEngagement);
    this.contextCache.put(key, new TransactionContext(KeyUtils.generateKeyPair() , Base64Utils.decode(base64EncodedClientEngagement.getBytes())));
    return this.getTransactionContextBy(key);
    }

// Part of retrieving RAS Key based on refId
public TransactionContext getTransactionContextBy(final String key) {
    return Optional.ofNullable(this.contextCache.getIfPresent(key)).orElseThrow(() -> {
    log.info("{} is empty", key);
    return new CustomException(HttpStatus.BAD_REQUEST, "No key matching the refId");
    });
}

2. Create request field values

@Override
public Mono<List<String>> createRequest(final PartnerInputDTO inputDTO) {
    final String mockData = "{ \"docType\": \"org.iso.18013.5.1.mDL\", \"nameSpaces\": { \"org.iso.18013.5.1\": { \"sex\": false, \"portrait\": false, \"given_name\": false, \"issue_date\": false, \"expiry_date\": false, \"family_name\": false, \"document_number\": false, \"issuing_authority\": false }, \"org.iso.18013.5.1.aamva\": { \"DHS_compliance\": false, \"EDL_credential\": false } } }";
    return Mono.just(Collections.singletonList(mockData));
}

3. Generate Establishment

@AllArgsConstructor
public class Establishment {
    private final TransactionContext context; // Info of client public key , partner private key, public key
    private final List<String> strReqs; // Data field information required for authentication to the client
    private final KeyRing keyRing; // RSA Key information for JWT(JWS + JWE) encryption and decryption between Wallet Backed Server and partner server.
 }

protected CBORObject generate() {
    final CBORObject sessionEstablishment = CBORObject.NewMap();
    sessionEstablishment.set(E_READER_KEY, CBORObject.FromObjectAndTag(KeyUtils.getEReaderKey(context), TAG_SIZE));  // Generate oneKey by public key in transactionContext
    sessionEstablishment.set(DATA, CBORObject.FromObject(CipherUtils.encrypt(context, generateRequestFormat(getRequestCBORObjectsFrom(strReqs))))); // Add request data field information for authentication.
    return sessionEstablishment;
}

Generate the response value JWT(JWS + JWE)

1. Generate establishment with JWE

public static String encryptedStringJWE(final Key publicKey, final String data) {  // Please enter Samsung Public Key and establishment data
    final JWEObject jwe = new JWEObject(
    new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128GCM).build(), new Payload(data));
    try {
        jwe.encrypt(new RSAEncrypter((RSAPublicKey) publicKey));
        return jwe.serialize();
    } catch (JOSEException e) {
        log.error("encryptedStringJWE Exception message : {}", e.getMessage());
        throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "encryptedStringJWE error");
    }
}

2. Generate JWS by JWE

public static String generateSignedStringJWS(final Key privateKey, final Key publicKey, final String payload) { // Enter your partner’s public key, private key, and JWE data.
  try {
    final JWSObject jwsObj = new JWSObject(getDefaultJWSHeader(), new Payload(payload));
    JWSSigner signer = new RSASSASigner(new RSAKey.Builder((RSAPublicKey) publicKey).privateKey((RSAPrivateKey) privateKey).build());
    jwsObj.sign(signer);
    return jwsObj.serialize();
  } catch (JOSEException e) {
    log.error("encryptedStringJWS Exception message : {}", e.getMessage());
    throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "generateSignedStringJWS error");
  }
}

3. Generate JWT(JWS + JWE)

public PartnerOutputDTO toPartnerOutputDTO() {
    final CBORObject generate = this.generate();
    final String establishment = Base64.getUrlEncoder().encodeToString(generate.EncodeToBytes());
    final String strJWE = JWTUtils.encryptedStringJWE(keyRing.getTargetPublicKey(), establishment);
    final JWSHeader jwsHeader = JWTUtils.getDefaultJWSHeader(keyRing.getVersion(), keyRing.getCertificateId(), "partnerId");
    return new PartnerOutputDTO(JWTUtils.generateSignedStringJWS(jwsHeader, keyRing.getSourcePrivateKey(), keyRing.getSourcePublicKey(),strJWE));
}

Authentication processing for values in data fields requested for authentication

1. Retrieve transactionContext value stored in cache using refId value

@Override
public Mono<TransactionContext> getContext(final PartnerInputDTO inputDTO) {
   return Mono.just(this.transactionContextManager.getTransactionContextBy(inputDTO.getRefId()));
}

2. Processes the decryption process of the request body data like JWT (JWS + JWE) decryption between Wallet Backed Server and partner server.

3. Generate MdocResponse

public class MdocResponse {
    private final TransactionContext context; // Managed tranactionContext by refId
    private final byte[] data; // Base64URL decoded data after decrypting JWT (JWS + JWE) data.
    
    public MdocResponse(final TransactionContext context, final String inputDTO) {
        this.context = context;
        this.data = Base64Utils.decode(inputDTO.getBytes(StandardCharsets.UTF_8));
    }
}

4. Get the field values requested for authentication from the data in mDocResponse.

public String getData() {
    // SessionData = {
    //     ? "data" : bstr ; Encrypted mdoc response or mdoc request
    //     ? "status" : uint ; Status code
    // }
    final CBORObject response = CBORObject.DecodeFromBytes(data);
    checkType(response, CBORType.Map);
    final CBORObject data = response.get(DATA);
    checkType(data, CBORType.ByteString);
    
    return CBORObject.DecodeFromBytes(isEncryptedMode ? CipherUtils.decrypt(this.context, data.GetByteString()) : data.GetByteString()).ToJSONString();
}

5. Create a Session value using the transactionContext value managed by refId and then decrypt it.

private static byte[] processCipher(final CipherMode cipherMode, final TransactionContext context, final byte[] bytes) { //  CipherMode : encrypt or decrypt, bytes : Data passed by the client
    try {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        final int counter = CipherMode.ENCRYPT == cipherMode ? context.getEncryptMessageCounter() : context.getDecryptMessageCounter();
        GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getSessionKeyIv(cipherMode.identifier, counter));
        cipher.init(cipherMode.cipherMode , getSecretKeySpec(context, cipherMode.info), parameterSpec);
        return cipher.doFinal(bytes);
    } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
        NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
        log.error("error type : {}, message :{}", e.getClass(), e.getMessage());
        throw new CustomException(HttpStatus.INTERNAL_SERVER_ERROR, "processCipher error");
    }
}

6. Examining data received from the client.

@Override
public Mono<Void> authentication(final String response) {
    log.info("response info : {}", response);
    return Mono.empty();
}

Wallet Cards

Refer to the Wallet Cards
This chapter defines Wallet Card data fields for the attributes object of each wallet card type.
The structure for configuring wallet cards follows the defined specification. Configuring the card data in the specified formatted JSON structure is required. See the details for each card type.

Type

Value

Description

Card object

card

Object

Required

Card information.

card.type

String(16)

Required

Wallet Card type

card.subType

String(16)

Required

allet Card sub type

card.data[]

Array of Object

Required

Wallet card data container
Allows up to 6 objects at once

data[].refId

String(32)

Required

A unique content identifier defined
by the content provider

data[].createdAt

Long(13)

Required

Data creation timestamp.
Epoch timestamp in milliseconds.
UTC±00:00

data[].updatedAt

Long(13)

Required

Data creation timestamp.
Epoch timestamp in milliseconds.
UTC±00:00

data[].language

String(8)

Required

Default content language code.
e.g., en, ko

data[].attributes

Object

Required

Card data attributes.
* Refer to the following chapters for each type

data[].attributes.{fields}

Attribute fields by card type.

data[].localization[]

Array of Object

Optional

Information for multilingual support.

localization[].language

String(8)

Required

Multilingual content language code.
e.g., en, ko, etc

localization[].attributes.{fields}

For displaying a given language,
‘data[].attributes’ can be replaced by
localized versions.
* Refer to the following chapters for each type

Example

Example: Card object
{
    "card": {
    "type": "ticket",
    "subType": "movies",
        "data": [{
        "refId": "ref-20230304-001",
        "createdAt": 1612660039000,
        "language": "en",
        "attributes": {
            "title": "Samsung Wallet",
            "mainImg": "https://../main.png"
            
            *Refer to Wallet Cards for each type
            
            },
            "localization": [{
            "language": "ko",
                "attributes": {
                "title": "삼성 월렛"
                }
            }]
        }]
    }
}

To ensure secure card data transmission, it must be tokenized in JWT format. For this purpose, partner will need the certificate obtained using the partner's email account when signing up for the partner portal.

For detailed information on secure data tokenization, partners can refer to the Security chapter.

*Image resources provided by URLs can be cached. Therefore, for the image resource to be replaced immediately, the corresponding URL path must be changed.

Relying Party

‘relyingparty’ cards are used for verifier authentication .

When partners create the Verify with Samsung Wallet button, they will need to create cdata.
This is the data spec included when creating the cdata of relayingparty type.

Wallet card type

Wallet card subtype

relyingparty

Others

Type

Value

Description

attributes{fields}

1

logoImage

String(256)

Required

URL of the logo image.
The file size must not be greater than 256 KB.

2

logoImage.darkUrl

String(256)

Required

URL of the logo image.
The file size must not be greater than 256 KB.

3

logoImage.lightUrl

String(256)

Required

URL of the logo image.
The file size must not be greater than 256 KB.

4

fontColor

String(8)

Optional

Display Color of the font during user authentication.

5

providerName

String(32)

Required

Display name during user authentication

6

clientType

String(32)

Required

Information on whether the client operates as an application or Web.

7

clientPackageName

String(32)

required

If the client is operating as an app, enter the package name, If it works on the web, enter a service name.

Example

{
    "card": {
    "type": "relyingparty",
    "subType": "others",
        "data": [
            {
            "refId": "0613001",
            "createdAt": 1686657600000,
            "updatedAt ": 1686657600000,
            "language": "en",
                "attributes": {
                "logoImage": "https://samsung.com",
                "logoImage.darkUrl": "https://dark.samsung.com",
                "logoImage.lightUrl": "https://light.samsung.com",
                "fontColor": "black",
                "providerName": "Company Name",
                "clientType": "app",
                "clientPackageName": "com.companyName.app"
                }
            }
        ]
    }
}