Samsung Pockets is a quick and safe digital pockets utility bundled with hundreds of thousands of Samsung Galaxy units worldwide. Its streamlined performance permits customers so as to add and retailer numerous passes, tickets, and credentials multi function place. You’ll be able to design customized Pockets playing cards, akin to for boarding passes, tickets, coupons, present playing cards and loyalty playing cards, to your service and problem them to customers who can add them to the Samsung Pockets utility on their system.
Samsung Pockets playing cards are signed with the RS256 uneven algorithm. The RS256 signing algorithm requires a non-public key, which is a private credential that must not ever be shared within the utility. Consequently, a separate server utility is required to retailer the important thing and signal the Pockets card knowledge (CData).
When the person faucets the “Add to Samsung Pockets” button within the utility, the server utility creates and indicators the Pockets card knowledge, then returns a JWT token that’s used so as to add the Pockets card to the person’s Samsung Pockets utility.
Determine 1: “Add to Pockets” stream
This tutorial makes use of Kotlin to reveal learn how to implement the “Add to Pockets” function in an Android cellular utility that provides film tickets to Samsung Pockets. It additionally exhibits learn how to generate the Pockets playing cards utilizing a Spring Boot server. You’ll be able to comply with together with the tutorial by downloading the pattern code information.
Develop the cellular utility
To implement the “Add to Pockets” button within the cellular utility, it’s essential to configure the applying, implement the applying UI, and outline the applying logic for the button.
Configuring the cellular utility challenge
Create an utility challenge and configure it to hook up with and talk with the server by REST API requests:
- In Android Studio, create a brand new challenge.
- To implement REST API help within the utility, add the next Retrofit library dependencies to the applying’s “construct.gradle” file:
'com.squareup.retrofit2:retrofit:2.11.0' 'com.squareup.retrofit2:converter-gson: 2.11.0'
- To allow communication with the server, add the next permissions to the “AndroidManifest.xml” file:
<uses-permission android:title="android.permission.INTERNET"/> <uses-permission android:title="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:title="android.permission.ACCESS_WIFI_STATE"/>
- To allow testing with a neighborhood non-HTTPS server, add the next attribute to the “utility” ingredient within the “AndroidManifest.xml” file:
android:usesCleartextTraffic="true"
Implementing the applying UI
The applying UI consists of two screens: the film tickets checklist and a ticket element web page.
-
Within the utility code, outline a
Film
knowledge class that comprises particulars for the film tickets that may be added to Samsung Pockets.knowledge class Film( val title:String, val studio:String, val ticketNumber:String, )
-
Create a
RecyclerView
that shows the checklist of film tickets as buttons on the principle display screen. Every button has a listener that opens the element web page for the ticket. You’ll be able to research the implementation particulars within the pattern code. -
To verify whether or not the system helps Samsung Pockets, ship an HTTP GET request to the next endpoint, the place
Construct.MODEL
is the mannequin of the system:https://api-us3.mpay.samsung.com/pockets/cmn/v2.0/system/accessible?serviceType=WALLET&modelName=${Construct.MODEL}
-
Create the ticket element web page format within the “activity_movie_detail.xml” file. The “Add to Pockets” button is applied on this web page.
Determine 2: Element web page format
Implement the ticket element web page performance within the
MovieDetailActivity
exercise class within the utility code.For this demonstration, the film ticket knowledge is predefined within the utility code. In an actual utility, the information is normally retrieved in actual time from an exterior database.
val movieLists = listOf<Film>( Film("The Pockets", "Samsung Studios", "A-01"), Film("Crying Sea", "Laplace Studio","H-07"), Film("Canoe", "Terra Productions", "R-03") ) val place:Int = intent.getIntExtra("moviePosition", 0) val film:Film = movieLists[position] binding.mvNameText.textual content = film.title binding.mvStudioText.textual content = film.studio binding.mvTicketNumber.textual content = "Ticket: ${film.ticketNumber}" binding.addToWalletButton.setOnClickListener { // Request server to generate card knowledge // Retrieve signed card knowledge from server // Add card to Samsung Pockets utility }
-
When the person faucets the “Add to Pockets” button,
OnClickListener()
is triggered. Its performance is outlined later on this tutorial.
Connecting to the server
To speak with the server:
-
Within the
TokenResponse
class, outline the construction of the JSON response to be acquired from the server. Thestanding
area signifies whether or not the token era request was profitable, and thejwt
area comprises the generated CData within the type of a JWT token.knowledge class TokenResponse( val standing: String, val jwt:String )
-
Within the “ApiClient.kt” file, outline a
RetrofitClient
object that’s used to ascertain the reference to the server. -
Outline the
ApiInterface
interface, which defines the API request and response:-
The API endpoint URL is
BASE_URL/film/{ID}
, the place{ID}
is the film ticket ID to be added -
The anticipated response from the endpoint is a
TokenResponse
object.
-
-
Outline an
ApiClient
object that extendsApiInterface
and creates aRetrofitClient
occasion to ascertain the server connection.object RetrofitClient { personal const val BASE_URL = "http://192.xxx.xxx.xxx:8080" // Outline your server URL val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .construct() } } interface ApiInterface { @GET("/film/{id}") droop enjoyable getMovie(@Path("id") movieId:Int): Response<TokenResponse> } object ApiClient { val apiService: ApiInterface by lazy { RetrofitClient.retrofit.create(ApiInterface::class.java) } }
Including card knowledge to Samsung Pockets
To request CData era from the server and add the Pockets card to Samsung Pockets:
-
Within the
addToWalletButton.setOnClickListener()
methodology inside theMovieDetailActivity
class, ship a request to the server to generate CData for the chosen film ticket. -
If CData era is profitable, so as to add the film ticket to Samsung Pockets, ship an HTTP request containing the CData token to the next endpoint URL:
https://a.swallet.hyperlink/atw/v1/{Card Id}#Clip?cdata={CData token}
-
For extra details about this endpoint, see Information Transmit Hyperlink.
binding.addToWalletButton.setOnClickListener {
CoroutineScope(Dispatchers.Important).launch {
val response = ApiClient.apiService.getMovie(place)
if(response.isSuccessful && response.physique()!=null){
startActivity(Intent(
Intent.ACTION_VIEW,
Uri.parse("http://a.swallet.hyperlink/atw/v1/3aabbccddee00#Clip?cdata=${response.physique()!!.jwt}")))
// Change '3aabbccddee00' half together with your card ID
}
}
}
ObserveThe generated CData is legitimate for 30 seconds, so it is suggested to generate the CData solely when the “Add to Samsung Pockets” button is clicked. If the CData has expired by the point the token is distributed to Samsung Pockets, the person can obtain a “Request Timed Out” error.
Generate signed Pockets card knowledge
The server utility should be configured to obtain the cardboard knowledge request from the cellular utility and return a signed JWT token. This a part of the tutorial makes use of the Spring Boot framework.
Configuring the server challenge
To create and configure a server utility to generate and signal Pockets card knowledge:
- Within the Spring Initializr software or any supported Java IDE, create a Spring Boot challenge and open the pattern code.
- To configure the server to obtain REST API requests from the cellular utility, add the “Spring Net” dependency to the challenge.
-
Outline a
Token
knowledge class. Make certain it has the identical attributes because theTokenResponse
knowledge class outlined within the cellular utility.knowledge class Token(val standing:String, val jwt:String
-
Initialize a
TokenController
class that receives the incoming requests and returns a Token object in response.@RestController @RequestMapping("film") class TokenController { @GetMapping(path = ["/{movieId}"]) enjoyable getMovie(@PathVariable movieId:Int): Token { return Token("success", "{DUMMY_CDATA}") // CData era logic } }
The CData era and signing logic is described within the subsequent part.
Implementing card knowledge signing logic
For simpler understanding, this part describes a simplified implementation of the CData Technology Pattern Code.
- Within the server utility challenge, copy the next credential information to the “pattern/securities/” listing.
- Samsung public key from the Samsung certificates (“Samsung.crt”)
- Accomplice public key out of your companion certificates (“Accomplice.crt”)
- Accomplice personal key from the personal key file (“Accomplice.key”)
- To deal with the certificates information and signing algorithms, add the next dependencies to the server utility’s “construct.gradle” file:
implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3' implementation 'org.bouncycastle:bcprov-jdk18on:1.77'
-
In a brand new “JwtGen.kt” file, outline a
readCertificate()
methodology that reads the general public keys from the certificates and areadPrivateKey()
methodology that reads the personal key from the important thing file.personal val PARTNER_ID = "4048012345678912345" // Change together with your companion ID personal val samsungPublicKey = readCertificate(getStringFromFile("pattern/securities/Samsung.crt")) personal val partnerPublicKey = readCertificate(getStringFromFile("pattern/securities/Accomplice.crt")) personal val partnerPrivateKey = readPrivateKey(getStringFromFile("pattern/securities/Accomplice.key")) enjoyable readPrivateKey(key: String): PrivateKey { val keyByte = readKeyByte(key) lateinit var privateKey: PrivateKey val pkcs8Spec = PKCS8EncodedKeySpec(keyByte) strive { val kf = KeyFactory.getInstance("RSA") privateKey = kf.generatePrivate(pkcs8Spec) } catch (e: InvalidKeySpecException) { e.printStackTrace() } catch (e: NoSuchAlgorithmException) { e.printStackTrace() } return privateKey } enjoyable readCertificate(cert: String): PublicKey { lateinit var certificates: Certificates val keyByte = readKeyByte(cert) val `is`: InputStream = ByteArrayInputStream(keyByte) strive { val cf = CertificateFactory.getInstance("X.509") certificates = cf.generateCertificate(`is`) } catch (e: CertificateException) { e.printStackTrace() } return certificates.publicKey } personal enjoyable readKeyByte(key: String): ByteArray { val keyByte: ByteArray val bais = ByteArrayInputStream(key.toByteArray(StandardCharsets.UTF_8)) val reader: Reader = InputStreamReader(bais, StandardCharsets.UTF_8) val pemReader = PemReader(reader) var pemObject: PemObject? = null strive { pemObject = pemReader.readPemObject() } catch (e: IOException) { e.printStackTrace() } keyByte = if (pemObject == null) { Base64.getDecoder().decode(key) } else { pemObject.content material } return keyByte } enjoyable getStringFromFile(path: String?): String { strive { val file = File(Objects.requireNonNull(ClassLoader.getSystemClassLoader().getResource(path)).file) return String(Information.readAllBytes(file.toPath())) } catch (e: IOException) { throw RuntimeException(e) } }
Producing card knowledge
CData token era is applied within the “JwtGen.kt” file:
- Learn the file containing uncooked JSON knowledge that defines the ticket knowledge construction.
For this demonstration, use the “Ticket.json” file within the “pattern/payload/” listing of the CData era pattern code.
- Generate or fill within the required ticket particulars. For instance, the “{title}” and “{seatNumber}” fields are changed with the film title and seat quantity.
For details about the entire JSON construction, see Pockets Playing cards.
- Convert the JSON knowledge to a JWE object.
- Encrypt the JWE object with the Samsung public key.
- Construct the customized JWS header for Samsung Pockets playing cards.
- Signal and validate the entire JWS object together with your companion personal and public key utilizing the RS256 uneven algorithm. That is the CData token.
personal val currentTimeMillis = System.currentTimeMillis()
personal val plainData:String = getStringFromFile("pattern/payload/Ticket.json")
.exchange("{refId}", UUID.randomUUID().toString())
.exchange("{language}", "en")
.exchange("{createdAt}", currentTimeMillis.toString())
.exchange("{updatedAt}", currentTimeMillis.toString())
.exchange("{issueDate}", currentTimeMillis.toString())
.exchange("{startDate}", (currentTimeMillis + TimeUnit.DAYS.toMillis(1)).toString())
.exchange("{endDate}", (currentTimeMillis + TimeUnit.DAYS.toMillis(1) + +TimeUnit.HOURS.toMillis(2)).toString())
enjoyable generateCdata(movieName: String, movieTicktNo:String): String{
// Modify knowledge as wanted
val knowledge = plainData.exchange("{title}", ""$movieName"")
.exchange("{seatNumber}",""$movieTicktNo"")
//print(knowledge)
return generate(PARTNER_ID, samsungPublicKey, partnerPublicKey, partnerPrivateKey, knowledge)
}
personal enjoyable generate(partnerId: String, samsungPublicKey: PublicKey, partnerPublicKey: PublicKey,
partnerPrivateKey: PrivateKey, knowledge: String): String {
val jweEnc = EncryptionMethod.A128GCM
val jweAlg = JWEAlgorithm.RSA1_5
val jweHeader = JWEHeader.Builder(jweAlg, jweEnc).construct()
val encryptor = RSAEncrypter(samsungPublicKey as RSAPublicKey)
val jwe = JWEObject(jweHeader, Payload(knowledge))
strive {
jwe.encrypt(encryptor)
} catch (e: JOSEException) {
e.printStackTrace()
}
val payload = jwe.serialize()
val jwsAlg = JWSAlgorithm.RS256
val utc = System.currentTimeMillis()
val jwsHeader = JWSHeader.Builder(jwsAlg)
.contentType("CARD")
.customParam("partnerId", partnerId)
.customParam("ver", "2")
.customParam("utc", utc)
.construct()
val jwsObj = JWSObject(jwsHeader, Payload(payload))
val rsaJWK = RSAKey.Builder(partnerPublicKey as RSAPublicKey)
.privateKey(partnerPrivateKey)
.construct()
val signer: JWSSigner
strive {
signer = RSASSASigner(rsaJWK)
jwsObj.signal(signer)
} catch (e: JOSEException) {
e.printStackTrace()
}
return jwsObj.serialize()
}
Returning the signed token
Within the server utility code, when the server receives a request on the film/{movieID}
endpoint, the TokenController
class calls the JwtGen.generateCdata()
methodology with the film ID, which generates and returns the CData JWT token within the API response.
On this tutorial, because the film ticket checklist was predefined within the cellular utility challenge, be certain the identical Film knowledge class and checklist are outlined right here too.
@RestController
@RequestMapping("film")
class TokenController {
@GetMapping(path = ["/{movieId}"])
enjoyable getMovie(@PathVariable movieId:Int): Token {
val movieLists = listOf<Film>(
Film("The Pockets", "Samsung Studios", "A-01"),
Film("Crying Sea", "Laplace Studio","H-07"),
Film("Canoe", "Terra Productions", "R-03")
)
if( movieId>2){
// Implement your verification logic
return Token("failure", "")
}
else{
val cdata = JwtGen.generateCdata(movieLists[movieId].title, movieLists[movieId].ticketNumber)
return Token("success", cdata)
}
}
}
Testing the applying
To check your “Add to Pockets” integration:
- Join the server and the cellular system to the identical community.
- Launch the server and cellular purposes.
- Within the cellular utility, faucet a film ticket within the checklist. Its element web page opens.
- Faucet Add to Samsung Pockets. The server generates and returns the CData token.
- The Samsung Pockets utility launches on the system and the film ticket data is added to it.
Determine 3: Ticket added to Samsung Pockets
Abstract
Implementing the “Add to Pockets” function allows your customers so as to add your digital content material, akin to tickets, passes, and loyalty playing cards, to the Samsung Pockets utility on their cellular system as Pockets playing cards. Along with implementing the “Add to Samsung Pockets” button in your cellular utility, it’s essential to additionally create a server utility that securely generates and indicators the Pockets card knowledge and returns it to the cellular utility for transmitting to Samsung Pockets.
For extra details about including “Add to Pockets” to your utility, see Implementing ATW button. You may also research the prolonged pattern utility (clicking this hyperlink downloads the pattern code) and the API reference.
When you’ve got questions on or need assistance with the data offered on this article, you’ll be able to share your queries on the Samsung Builders Discussion board. You may also contact us instantly for extra specialised help by the Samsung Developer Assist Portal.
Assets
Click on the hyperlinks under to obtain the pattern code.