Filter
-
Content Type
-
Category
Mobile/Wearable
Visual Display
Digital Appliance
Platform
Mobile/Wearable
Visual Display
Digital Appliance
Platform
Filter
Learn Code Lab
codelabintegrate samsung pay web checkout with merchant sites objective learn how to integrate the samsung pay payment system into your merchant sites using the samsung pay web checkout sdk partnership request to use the samsung pay web checkout sdk, you must become an official samsung pay partner once done, you can fully utilize this code lab you can learn more about the partnership process by visiting the samsung pay page here in samsung developers notein accordance with the applicable samsung pay partners agreements, this code lab covers the setup and use of the samsung pay web checkout sdk for purposes of integrating samsung pay with merchant sites the use cases and corresponding code samples included are representative examples only and should not be considered as either recommended or required overview the samsung pay web checkout service enables users to pay for purchases on your website with payment cards saved in the samsung wallet app on their mobile device it supports browser-based payments on both computers and mobile devices a mobile device with samsung wallet installed is required to make purchases through samsung pay web checkout when the user chooses to pay with samsung pay, they must provide their samsung account id email id or scan the qr code on the screen with their mobile device the user then authorizes the purchase within the samsung wallet application, which generates the payment credential on the device and transmits it to your website through the web checkout for more information, see samsung pay web checkout set up your environment you will need the following access to samsung pay developers site samsung wallet test app from samsung pay developers site samsung galaxy device that supports samsung wallet app internet browser, such as google chrome codesandbox account notein this code lab, you can use the samsung wallet test app to try the functionality of the samsung pay web checkout service in a staging environment you can use the official samsung wallet app from the galaxy store once your service is in the production environment start your project and register your service in your browser, open the link below to access the project file of the sample merchant site codesandbox io/s/virtual-store-sample-fnydk5 click the fork button to create an editable copy of the project next, follow the steps below to register your sample merchant site in the samsung pay developers site go to my projects > service management click create new service select web online payment as your service type enter your service name and select your service country select your payment gateway from the list of supported payment gateways pg if your pg uses the network token mode, upload the certificate signing request csr or privacy enhanced mail pem file you obtained from your pg contact your pg for details enter the payment domain name s for your website in the service domain field and click add for example, if your domain is mywebstore com, but the checkout page is hosted on the subdomain payments mywebstore com, you will need to enter payments mywebstore com as the service domain for each additional domain url, click add in this code lab, the generated preview url of the forked project is your service domain click the name of the newly created service to see its details, such as the generated service id that you can use for all the registered service domains include the samsung pay web checkout javascript sdk the samsung pay web checkout sdk uses javascript to integrate the samsung pay payment system to your website this sdk allows users to purchase items via web browser in the <head> section of the index html file of the project, include the samsung pay web checkout javascript sdk file <script src="https //img mpay samsung com/gsmpi/sdk/samsungpay_web_sdk js"></script> initialize the samsung pay client to initiate payments using the samsung pay api, create a new instance of the paymentclient class and pass an argument specifying that the environment as stage write the code below in the <script> tag of the <body> section const samsungpayclient = new samsungpay paymentclient { environment "stage" } ; when the service is still in debug or test mode, you can only use the staging environment to test payment functionality without processing live transactions noteby default, the service is initially set to debug or test mode during creation to switch the service status to release mode, a request must be made through the samsung pay developers site after successfully transitioning to release mode, change the environment to production next, define the service id, security protocol, and card brands that the merchant can support as payment methods the service id is the unique id assigned to your service upon creation in the samsung pay developers site let paymentmethods = { version "2", serviceid "", //input your service id here protocol "protocol_3ds", allowedbrands ["visa", "mastercard"] }; check whether the samsung pay client is ready to pay using the given payment method call the createandaddbutton function if the response indicates that the client is ready samsungpayclient isreadytopay paymentmethods then function response { if response result { createandaddbutton ; } } catch function err { console error err ; } ; create and implement the samsung pay button go to the <body> section and, inside the page-container div, create a container for the samsung pay button <div align="center" id="samsungpay-container"></div> next, go back to the <script> tag and write the createandaddbutton function inside this function, generate the samsung pay button by calling the createbutton method ensure that the button appears on the page by appending it to the container you created function createandaddbutton { const samsungpaybutton = samsungpayclient createbutton { onclick onsamsungpaybuttonclicked, buttonstyle "black"} ; document getelementbyid "samsungpay-container" appendchild samsungpaybutton ; } function onsamsungpaybuttonclicked { // create the transaction information //launch the payment sheet } from the createandaddbutton function, call the onsamsungpaybuttonclicked function when the user clicks the generated button create the transaction information in the onsamsungpaybuttonclicked function, create the transactiondetail object for the user’s purchase input your service domain in the url key let transactiondetail = { ordernumber "sample0n1y123", merchant { name "virtual shop", url "", //input your service domain countrycode "us" }, amount { option "format_total_estimated_amount", currency "usd", total 2019 99 } }; below are the descriptions of the keys included in the transactiondetail object key type description ordernumber string order number of the transaction allowed characters [a-z][a-z][0-9,-] merchant object data structure containing the merchant information merchant name string merchant name merchant url string merchant domain url e g , samsung com the maximum length is 100 characters merchant countrycode string merchant country code e g , us for united states iso-3166-1 alpha-2 amount object data structure containing the payment amount amount option string display format for the total amount on the payment sheet format_total_estimated_amount = displays "total estimated amount " with the total amountformat_total_price_only = displays the total amount only amount currency string currency code e g , usd for us dollar the maximum length is 3 character amount total string total payment amount in the currency specified by amount currencythe amount must be an integer e g , 300 or in a format valid for the currency, such as 2 decimal places after a separator e g , 300 50 notefor the complete list of specifications for the transactiondetail object, see samsung pay web checkout api reference launch the payment sheet after creating the transaction information, call the loadpaymentsheet method to display the web checkout ui the user can either input their email address or scan the generated qr code a timer screen in the web checkout ui is displayed after the user input, while a payment sheet is launched in the user's samsung wallet app the payment sheet contains the payment card option s and the transaction details when the user confirms their payment on their mobile device, you will receive the paymentcredential object generated by the device then, inform the samsung server of the payment result using the notify method the paymentresult object contains the payment result information during transaction processing and after the payment is processed with the pg network notefor real transactions, you need to extract the payment credential information from the 3ds data key within the paymentcredential object and process it through your payment provider however, in this code lab, you only need to print the paymentcredential to the console samsungpayclient loadpaymentsheet paymentmethods, transactiondetail then function paymentcredential { console log "paymentcredential ", paymentcredential ; const paymentresult = { status "charged", provider "test pg" }; samsungpayclient notify paymentresult ; } catch function error { console log "error ", error ; } ; other possible values of the status key are charged - payment was charged successfully canceled - payment was canceled by either the user, merchant, or the acquiring bank rejected - payment was rejected by the acquiring bank erred - an error occurred during the payment process test the samsung pay button after integrating the samsung pay web checkout service into your sample merchant site, follow the steps below to test the functionality of the integrated service open your sample merchant site in a new tab then, click the pay with samsung pay button in the web checkout ui, enter the email address of your samsung account to send a payment request to samsung pay tap the push notification sent to the samsung wallet app installed on your mobile device then, click accept when the payment sheet is loaded, tap on pin and enter your pin to proceed a verified message will display in both the samsung wallet app and web checkout ui to indicate that the payment was processed successfully you're done! congratulations! you have successfully achieved the goal of this code lab topic now, you can integrate the samsung pay web checkout service into your website by yourself if you're having trouble, you may check the complete code below codesandbox io/s/virtual-store-complete-dkhzfx to learn more, explore samsung pay
tutorials mobile
blogin a previous blog article, we learned how we could utilize samsung wallet's rp sdk in order to verify a user's identity from an android application. in this tutorial, we learn how to verify a user's identity directly from a website using samsung wallet and its web2app api in a spring boot web server. prerequisites the process described has the following prerequisites: a valid us driver's license or state id for the user whose identity is to be verified the samsung galaxy device used needs to be registered for the us region and have mdl support complete the samsung wallet partner onboarding process create a wallet card template with the relying party type in the samsung wallet partners portal implementing the verify with wallet functionality in your website the verify with wallet (vww) process utilizing the web2app method consists of two distinct parts. the "verify with samsung wallet" button. this button contains the vww link with the rp card data tokenized as the cdata. the user can click this vww link to initiate the verification process. the partner server containing the /key and /auth endpoints. the partner server processes the requests sent from the samsung wallet application and handles the complete vww process. frontend configuration in order to initiate the verify with wallet process, we need to implement a "verify with samsung wallet" button in a webpage. implementing the button is a very simple process similar to creating a traditional "add to wallet" button. we can make use of the data transmit link approach and create a button that contains the vww link: https://a.swallet.link/vww/v1/{cardid}#clip?cdata={cdata} replace {cardid} with the id of your own card. meanwhile, the cdata value needs to be generated in real time. this is done using a process similar to generating cdata for atw operation, only with the payload contained being different according to the specification for the relying party card type. check out the sample code for the complete process of cdata generation and using it in the button. backend configuration once the button implementation is complete, you need to configure your server to handle the exchange of information between your server and samsung wallet application. the vww process requires the partner to define 2 api endpoints: /rp/v1.0/{cardid}/{refid}/key: establishes a secure session and prepares the request data for the process. /rp/v1.0/{cardid}/{refid}/auth: processes encrypted authentication data and mdl data received from the wallet application. the workflow for the information exchange is as follows: once the vww button is clicked, the samsung wallet application opens. samsung wallet checks if the device has a driver's license already enrolled in the device. if an mdl already exists on the device, the samsung wallet application calls the /key endpoint to establish a session. after establishing session with the partner server and retrieving the mdoc request, the samsung wallet application prompts the user to confirm if they wish to share their information with the partner. after the user confirms that they wish to proceed, the application finally sends the requested information to the /auth api to complete the vww process. define the /key endpoint when the user clicks the "verify with samsung wallet" button, the samsung wallet application first checks if a driver's license is enrolled. if a license is found, the application generates "device engagement bytes" in accordance with the iso-18013-5 specification. these bytes are then transmitted to the server's /key api endpoint to establish a verification session. the post request body is json with a single field called data. this field value is the jwt containing encrypted device engagement bytes. {"data": "………"} in the /key api endpoint, accept the post request sent to the path /{cardid}/{refid}/key extract the data field from the body as the jwt and decrypt it to receive the device engagement bytes. establish a session using the device engagement bytes. create the mdoc request data and send it back to the samsung wallet application as response to the post request. the complete process is shown below: @postmapping("{cardid}/{refid}/key") fun receivekey( @pathvariable cardid: string, @pathvariable refid: string, @requestbody body: string ): responseentity<string> { val cdata = jsonparser.parsestring(body).asjsonobject.get("data").tostring() val base64engagementbytes = jwtgen.decryptbase64engagement(cdata) val mdoc18013 = createmdoc10813(base64engagementbytes) val cdataresponse = "{\"data\": \"${jwtgen.generaterequestjwt(mdoc18013)}\"}" return responseentity.ok().contenttype(mediatype.application_json).body(cdataresponse) } decrypt the device engagement bytes from the request body the data field value received in the /key api contains the required device engagement bytes encoded in the jwt format. simply decrypt the jwt in order to retrieve the device engagement bytes. here the decryptbase64engagement() function is defined as follows: fun decryptbase64engagement(data: string): bytearray { val signedjwt: signedjwt = signedjwt.parse(data) val payload = signedjwt.payload val jwe = jweobject.parse(payload.tostring()) val partnerprivatekey = keyutil.readprivatekey(partner_private_key) val decrypter = rsadecrypter(partnerprivatekey) jwe.decrypt(decrypter) val base64engagement = jwe.payload.tojsonobject().get("data").tostring() val base64engagementbytes = base64.geturldecoder().decode(base64engagement) return base64engagementbytes } simply perform the jwt decryption operation using your private key to get the decrypted jwe payload in the json format. in the json-formatted payload, the data field contains the device engagement bytes encoded in the base64url string format. decode the string using a base64url decoder and you get the final device engagement bytes. create a shared session using device engagement bytes the createmdoc10813(base64engagementbytes) function creates a shared session between the /key api and /auth api using a companion object. having a shared session between the two endpoints is mandatory in order to decrypt the information provided by the samsung wallet application later on. inside the companion object, we also need to generate an elliptic curve keypair in order to establish the encrypted session. the companion object is defined as shown below: companion object{ val keypair = keyutil.generateeckeypair() var mdoc18013: mdoc18013? = null fun createmdoc10813(base64engagementbytes: bytearray): mdoc18013 { if (mdoc18013 == null ) { mdoc18013 = mdoc18013(keypair, base64engagementbytes ) return mdoc18013!! } else{ return mdoc18013!! } } fun getmdoc10813(): mdoc18013 { return mdoc18013!! } } the elliptic curve keypair is generated using a simple keypairgenerator class instance. fun generateeckeypair(): keypair { val keypairgenerator = keypairgenerator.getinstance("ec") val ecgenparameterspec = ecgenparameterspec("secp256r1") keypairgenerator.initialize(ecgenparameterspec) return keypairgenerator.generatekeypair() } prepare the mdoc request data preparing the mdoc request data is the most crucial part of the vww operation. the request data defines the data that needs to be retrieved from mdl. the generaterequestjwt() function can be divided into several parts: define and encode the request data payload. encrypt the device request. create session establishment data using the encrypted device request bytes. create a signed jwt. below, we go through these steps one at a time. define the request data payload and encode it to a cbor byte array // define requested data fields val requestdata = """ { "doctype": "org.iso.18013.5.1.mdl", "namespaces": { "org.iso.18013.5.1": { "family_name": true, "age_in_years": true, "issue_date": true, "expiry_date": true, "document_number": false, "issuing_authority": false }, "org.iso.18013.5.1.aamva": { "dhs_compliance": false } } } """.trimindent() // cbor encoding process with tagging val firstencoded = cborobject.fromjsonstring(requestdata).encodetobytes() val thirdencoded = cborobject.fromobjectandtag(firstencoded, 24).encodetobytes() val itemrequestbyteslist = listof(thirdencoded) // create mdoc items requests array val docrequestsarray = cborobject.newarray() itemrequestbyteslist.foreach { val docrequest = cborobject.newmap() docrequest.set("itemsrequest", cborobject.decodefrombytes(it)) docrequestsarray.add(docrequest) } // create device request using docrequestarray val devicerequest = cborobject.newmap() devicerequest.set("version", cborobject.fromobject("1.0")) devicerequest.set("docrequests", docrequestsarray) encrypt the device request val encrypteddevicerequestbytes = mdoc18013.encryptdevicerequest(devicerequest.encodetobytes()) create session establishment data using the encrypted device request bytes val establishment = cborobject.newmap() establishment.set("ereaderkey", cborobject.fromobjectandtag(mdoc18013.getereaderkey(),24)) establishment.set("data", cborobject.fromobject(encrypteddevicerequestbytes)) val establishmentstring = base64.geturlencoder().encodetostring(establishment.encodetobytes()) create a signed jwt using the establishmentstring as the jwe payload val jweobj = jweobject(jweheader.builder(jwealgorithm.rsa_oaep_256, encryptionmethod.a128gcm).build(), payload(establishmentstring)) val encryptor = rsaencrypter(samsungpublickey as rsapublickey) jweobj.encrypt(encryptor) val jwsheader = jwsheader.builder(jwsalgorithm.rs256) .contenttype("auth") .customparam("partnerid", "412255212345678910") .customparam("certificateid", "a123") .customparam("ver", "3") .customparam("utc", system.currenttimemillis()) .build() val jwsobj = jwsobject(jwsheader, payload(jweobj.serialize())) val rsajwk = rsakey.builder(partnerpublickey as rsapublickey).privatekey(partnerprivatekey).build() val jwssigner = rsassasigner(rsajwk) jwsobj.sign(jwssigner) return jwsobj.serialize() now, we can send this jwt back as the response from the /key api. if everything is done properly, the samsung wallet application should receive the verification request along with the list of requested fields. after processing and verifying the request, the samsung wallet application needs to prompt the user to verify their identity. once the user verifies their identity using the application, it sends the requested information back to the /auth api endpoint. next, let's define the /auth api endpoint to retrieve the requested information. define the /auth api endpoint similar to the previously defined /key api endpoint, the /auth api endpoint also receives a single json payload with a single field called data, which contains the requested information in a jwt encoded format. {"data": "………"} decrypt the jwt payload from the request body we can extract the data field and decrypt the jwt following the same process used in the /key api. @postmapping("{cardid}/{refid}/auth") fun receiveauth( @pathvariable cardid: string, @pathvariable refid: string, @requestbody body: string ): httpstatus { val responsedata = jsonparser.parsestring(body).asjsonobject.get("data").tostring() val signedjwt: signedjwt = signedjwt.parse(responsedata) val payload = signedjwt.payload val jwe = jweobject.parse(payload.tostring()) val partnerprivatekey = jwtgen.partnerprivatekey val decrypter = rsadecrypter(partnerprivatekey) jwe.decrypt(decrypter) // process and decrypt the data until the requested information is retrieved return httpstatus.ok } after the decryption operation, we get another json object in the decrypted jwe payload. in this json payload, the data field contains the encoded data of the information we requested. to decode and decrypt this data: decode the extracted data field value using base64url decoder. this gives us the encrypted mdoc response in a cbor byte array. decode the cbor object from the byte array and get the mdoc data from the data field. decrypt the mdoc data using the mdoc18013.decryptmdocresponse() function to retrieve the plain response in the json format. warningthe mdoc18013 instance used for this step must be the same instance used in the /key api. otherwise, the decryption operation fails. val mdoc18013 = getmdoc10813() val cbordata = jwe.payload.tojsonobject().get("data").tostring() val decodeddata = base64.geturldecoder().decode(cbordata) val mdocresponse = cborobject.decodefrombytes(decodeddata) val mdocdata = mdocresponse.get("data") val decryptmdocresponsebytes = mdoc18013.decryptmdocresponse(mdocdata.getbytestring()) val plainresponse = cborobject.decodefrombytes(decryptmdocresponsebytes).tojsonstring() after these steps, we finally have the mdoc response in a plain json format. { "status": 0, "version": "1.0", "documents": [ { "doctype": "org.iso.18013.5.1.mdl", "devicesigned": {}, "issuersigned": { "issuerauth": ["......."], "namespaces": { "org.iso.18013.5.1": [ "pghkawdlc3rjrbkhfwzyyw5kb21uczc4zny4c2nongmyzhr5mnlyotzxzwxlbwvudelkzw50awzpzxjsywdlx2lux3llyxjzbgvszw1lbnrwywx1zrgs", "pghkawdlc3………" ], "org.iso.18013.5.1.aamva": [ "pghkawdlc3rjrblld2zyyw5kb21uczh5cmptbtu4ohmynzy4emoznm5xzwxlbwvudelkzw50awzpzxjurehtx2nvbxbsawfuy2vszwxlbwvudfzhbhvlyuy" ] } } } ] } here, the values inside the org.iso.18013.5.1 and org.iso.18013.5.1.aamva are the fields we initially requested in the key api. simply decode these cbor-encoded fields to retrieve the information you requested. for example, the "pghkawdlc3rjrbkhfwzyyw5kb21uczc4zny4c2nongmyzhr5mnlyotzxzwxlbwvudelkzw50awzpzxjsywdlx2lux3llyxjzbgvszw1lbnrwywx1zrgs" value informs us that element name is age_in_years and its value is "44," meaning the subject is 44 years old. we can extract the rest of the requested information by decoding the other provided values in the same way. figure 1: verifying user identity using vww web2app process summary in this tutorial, we learned how we can implement user identity verification on a website utilizing samsung wallet's verify with wallet functionality. by making use of the web2app method discussed in this article, you can allow users to securely confirm and verify their digital identity using their mobile driver's licenses. related resources iso/iec 18013-5:2021 - personal identification — iso-compliant driving licence — part 5: mobile driving licence (mdl) application mobile driver license - american association of motor vehicle administrators - aamva verify with wallet api guidelines relying party card specifications sample code download link
Mobassir Ahsan
Develop Samsung Browser
docweb payments integration guide overview to help standardize and streamline how payments are done on the web, the worldwide web consortium w3c has introduced a payment request api to provide an interface between a merchant web page and a mobile payment app, like samsung pay, to facilitate payment transactions samsung internet browser leverages the w3c payment request api to support samsung pay as a payment method for web purchases chrome also supports a samsung pay web payment method why integrate samsung pay into your website? samsung pay is accepted at more retail locations than any other mobile payment service available because of its unique ability to transact with newer nfc-supported payment terminals and legacy payment terminals it continues to enjoy the best user reviews among mobile payment apps now available for mobile website integration, samsung pay is secure, easy to set up, simple to use, and pre-installed on all new samsung galaxy-class smartphones when integrated with your website, samsung pay presents your users with a common checkout process that leverages samsung pay’s secure purchase authentication technology, eliminating manual entry or re-entry of card details and shipping destinations checkout is streamlined, conversions are maximized, and the exposure of sensitive data is kept to the absolute minimum simplifying the transaction process the benefits of the new process, certainly from an end-user perspective, is that the previous tedium ― request, authorization, payment, and result ― can now be handled in a single step for the web developer, it entails a single javascript api call for samsung pay users, there’s no change at all in the way a payment card is selected and authenticated after selecting the desired merchandise from the merchant’s web site, the user initiates checkout, selects samsung pay as the preferred payment method, authenticates with a fingerprint or pin, and voila ― payment complete that’s the user experience at its most basic when properly implemented, the api also supports editing the billing/shipping address in samsung pay and selecting a different enrolled card before approving the transaction with a fingerprint scan or entering a pin in terms of convenience, it’s a remote shopper’s dream ― no complicated, input-intensive forms to fill out, no fumbling for a plastic card to enter the account number, card expiration, and security code, and no worrying that someone other than the legitimate cardholder is attempting to make the payment meanwhile, samsung pay’s tokenized payload securely protects the transaction from intercept and replay attacks about this guide intended for web developers with a working knowledge of javascript and json, this guide takes you through the complete process of onboarding as a samsung pay merchant partner, creating/registering the w3c service for your domain, adding the w3c payment request object to your website, then testing and releasing your merchant website offering samsung pay as a payment method let's get started determining your gateway integration mode the api methods for integrating samsung pay with your website depend on the type of payment token your payment gateway pg handles — either gateway tokens or network tokens samsung pay supports requests for both types for instance, if stripe is your pg, you will want to request a gateway token from samsung pay on the other hand, if you’re using first data, you’ll want to request an encrypted network token bundle, for which you handle the token decryption yourself or work with the pg first data in this case to handle decrypting the token bundle the process begins when your merchant website makes a payment request and passes all required information to the browser, which then determines compatibility between the accepted payment methods for your website and the methods apps installed on the target device let’s take a brief look at how each integration mode — gateway token and network token — works with samsung pay gateway token mode although samsung pay doesn’t process the payment, your merchant website will still need to invoke the appropriate payment gateway apis to charge and process the token returned by your pg hence, when samsung pay returns a gateway token from stripe, for example, the recommended flow looks like this user selects samsung pay as the payment method at checkout in the merchant's website and the samsung pay app requests partner verification from the samsung pay online payment server encrypted payment information and the partner id are passed to the samsung-pg interface server samsung-pg interface server sends a transaction authorization request to the pg on behalf of the merchant; pg authenticates the partner id before generating a transaction reference id samsung-pg interface server returns the payment token to the pg i e , the gateway token it received from the samsung pay app in step 2 pg continues payment processing with the acquirer and payment network the result approved/declined is returned to the merchant website on the device for display to the user in this mode, samsung pay makes a call to your pg on your behalf and returns a chargeable gateway token network token mode under network token mode, the samsung pay api returns an encrypted network token bundle, which you can then either decrypt yourself or leverage the apis of your pg first data, for example to handle decryption and charge the token user selects samsung pay as the payment method at checkout in the merchant's website and the samsung pay app requests partner verification from the samsung pay online payment server encrypted payment information is passed from the samsung pay app to the pg through the merchant app via the pg sdk applying the merchant's private key, pg decrypts the payment information structure and processes the payment through the acquirer and payment network upon receiving authorization or rejection, pg notifies the merchant website through its pg sdk to simplify integration of network tokens, you can pass the encrypted payload directly to your pg and let it handle decryption in general, decrypting the payload yourself is more complex and involves private key management see your particular pg’s documentation for details once you determine which mode your pg supports, you're ready to register with the portal first, however, there are a number of prerequisites you'll need to satisfy prerequisites registering with the samsung pay developers portal and adapting the appropriate payment request apis for your website in accordance with the guidance contained herein will help to ensure a successful implementation to that end, the following requirements apply release contents minimum samsung pay app version 2 8 xx minimum samsung internet browser version 5 4 minimum chrome browser version 61 supported device models samsung galaxy-class smartphones running minimum version of the wallet app and browser supported payment gateways pgs view the most current list in the portal's payment gateway drop-down menu in service create/edit mode see step 5 under registering your domain for the w3c service registering with the portal through the samsung pay developers portal you can access valuable resources to help you manage the samsung pay features you incorporate into you partner app, including the ability to create multiple samsung pay service groups so you can use different services without the need to create multiple accounts invite co-workers to the portal to help you manage samsung pay features for your website register your website with samsung pay configure your samsung pay w3c service s to create a member account on the samsung pay developers portal open a browser like chrome, go to https //pay samsung com/developers, click sign up and confirm/acknowledge that you accept the samsung pay terms and conditions and understand the privacy policy, then do one of the following a if you already have a valid samsung account, click sign up and enter your samsung account id and password b if you do not have a samsung account, click create a samsung account to create one open the account activation email you receive and click the account activation link if you’re the first samsung pay member of your company to register, select the first option, click next, and complete a company and user profile if, on the other hand, you were given a samsung pay partner id by a co-worker, select the second option — my company is already registered — and enter your company’s partner id in the field provided, then click next after you receive notification by email that your membership is approved, typically within 2 business days, return to https //pay samsung com/developers, click sign in and enter your samsung account id and password for site access as a new member registering your domain for the w3c service your domain is the url associated with your website, whether in test or production the service type specifically associates w3c with your domain once you create the service, you will be prompted to configure the domain you want associated with it to create a new service go to my projects > service management and click create a new service select for test to define the service for initial integration with samsung pay, then click next select w3c mobile web payments as the service type and click next enter a service name and select “united states” as the service country select a payment gateway from the list of supported pgs if your payment gateway uses the network token mode, upload the certificate signing request csr you obtained from your pg supported formats are csr or pem contact your pg for details otherwise, key management is already established for pgs supporting samsung pay’s gateway token mode; hence, click connect with to create a new service connection with the pg and click ok enter the payment domain name s for your website in the service domain field and click add for example, if your domain is mywebstore com but the checkout page is hosted on the subdomain payments mywebstore com, you’ll need to enter payments mywebstore com as the service domain in the portal for each additional domain name, click add notewhen entering the domain name on the portal, do not include an https // prefix confirm your agreement with the terms and conditions, then click next adding w3c payment requests objects to your website in order to accept payments from samsung pay, your website must adhere to the w3c payment request api specification you can get started with the basics by completing the steps that follow and making the appropriate substitutions for your website step 1 feature detect prior making a w3c payment request, it’s wise to run a feature detect to ensure the browser in use supports the w3c payment request api if not, you should fall back to your traditional/normal checkout page example if window paymentrequest { // use payment request api } else { // fallback to traditional checkout window location href = ‘/checkout/traditional’; } step 2 create the paymentrequest constructor the first step is to create a paymentrequest object by calling the paymentrequest constructor, which has the following parameters methoddata – contains a list of payment methods that the website supports e g , visa, amex, samsung pay details – contains information about the shopping cart purchases e g , total, tax, fees, etc options – details pertaining to shipping address, user contact information, etc example var request = new paymentrequest methoddata, // required payment method data details, // required information about transaction options // optional parameter for things like shipping, etc ; step 3 add samsung pay to the methoddata parameter the methoddata parameter contains a list of payment methods supported by the merchant to support samsung pay, you’ll need to add it as a supported payment method notebasic-card is an optional supportedmethod for credit and debit cards saved in the browser, the card details of which are returned directly to the website from the browser before configuring this method, make sure your website and pg can handle generic payment information received from the browser and process with required pci compliance, if applicable see the [w3c basic card payment specification][7] for additional details be aware, however, that if you intend to support a branded samsung pay button, only samsung pay can be enabled as a payment method within the paymentrequest object; basic-card or any other payment method cannot be included if you already offer a generic payment request method, you can continue to do so — and include samsung pay as a payment method within that paymentrequest object from a user experience standpoint, the two distinct pathways look like this standard w3c implementation a standard w3c implementation adds samsung pay to your standard paymentrequest object as one of many supportedmethods available for user selection tapping checkout or its equivalent launches the standard browser payment sheet for user selection of the payment method and/or to add a debit/credit card to the list of options selecting samsung pay as the payment method and tapping pay launches the samsung pay payment sheet branded samsung pay implementation a branded implementation displays a "buy with samsung pay" button in place of your standard checkout button tapping the button will skip directly to the samsung pay payment sheet for authentication unless you specify paymentoptions if paymentoptions is not null, the browser payment sheet is launched see paymentoptions in step 7 for additional guidance first launching your website's checkout page is recommended for branded implementations this is because the samsung pay payment sheet only provides the user's billing address, which means your website will need to capture the user's preferred shipping address, where applicable for physical delivery of purchased goods if no shipping address and/or other options are needed, set the paymentoptions parameter to null as mentioned above, if you populate paymentoptions, the browser payment sheet is automatically launched keeping the foregoing implementations in mind, let's look at how to construct the methoddata argument for the network token mode and gateway token mode, respectively the fields in methoddata comprise supportedmethods – required; specifies https //spay samsung com/ i e , the samsung pay app and other methods suppored by your website data – values specific to the method; for samsung pay, these include version specifies the data structure being used by the merchant; should always be set to 1 until further notice required productid the service id obtained from the samsung pay partner portal required for partner verification; see step #8 under registering your domain for the w3c service merchantgatewayparameter this is the userid value registered with your pg required for gateway token mode in addition, userid should be set in the request parameter for mada token if a merchan request mada token, this field is mandatory as this filed should be included in the payload for mada token, there is a 15-character length limit paymentprotocol defaults to “protocol_3ds,” the only protocol currently supported by samsung pay optional allowedcardnetworks specifies the card brands/networks accepted by the merchant and supported by the pg required merchantname the name of the merchant to be displayed on the payment sheet; must be identical to the merchant name registered on the samsung pay partner portal required ordernumber unique value for merchant use as an external reference id optional isrecurring specifies transaction on subscription basis optional; default = false billingaddressrequired determines if a billing address must be filled-in by the user optional; default = false shown next are examples of the methoddata parameter for each of the supported token modes please note that samsung pay's w3c support for card brands is currently limited to mastercard, visa and american express discover is scheduled for support soon example – network token mode var methoddata = [ { supportedmethods ['https //spay samsung com'], data { 'version' '1', // always 1 until further notice 'productid' '2bc3e6da781e4e458b18bc', // service id from partner portal 'allowedcardnetworks' ['mastercard','visa'], 'merchantname' 'shop samsung demo ', // merchantname must be identical to merchant name on portal 'ordernumber' '1233123', } }] example – gateway token mode var methoddata = [ { supportedmethods ['https //spay samsung com'], data { 'version' '1', // always 1 until further notice 'productid' '7qr7h9ws1872bc3e6da781', // service id from partner portal 'merchantgatewayparameter' {"userid" "acct_ 17irf7f6ypzj7wor"}, 'allowedcardnetworks' ['mastercard','visa'], 'merchantname' 'shop samsung demo ', //merchantname must be identical with merchant name on portal 'ordernumber' '1233123', } }] step 4 fill out the transaction details parameter the details parameter contains information about the transaction there are two major components a total, which reflects the total amount and currency to be charged, and an optional set of displayitems that indicate how the final amount was calculated this parameter is not intended to be a line-item list, but is rather a summary of the order’s major components subtotal, discounts, tax, shipping costs, etc example var details = { displayitems [ { label "total of all items", amount { currency "usd", value "65 00" }, // us$65 00 }, { label "friends & family discount", amount { currency "usd", value "-10 00" }, // -us$10 00 pending true // the price is not yet determined } ], total { label "total", amount { currency "usd", value "55 00" }, // us$55 00 } } step 5 check eligibility to display samsung pay button if you do not support basic-card and you try to call show in step 6 when samsung pay or any other supportedmethod is not present on the device, the returned promise will reject with the following error domexception the payment method is not supported you can, however, check beforehand to see if the user has an available/supported method set up this is done with the canmakepayment method, which tells you whether the user has a payment method that can fulfill the current payment request example – canmakepayment const paymentrequest = new paymentrequest supportedpaymentmethods, transactiondetails, options ; // if canmakepayment isn’t available, default to assume the method is supported const canmakepaymentpromise = promise resolve true ; // feature detect canmakepayment if request canmakepayment { canmakepaymentpromise = paymentrequest canmakepayment ; } canmakepaymentpromise then result => { if !result { // the user does not have a supported payment method // todo redirect to traditional checkout flow return; } // todo the user has a payment - call show } catch err => { // todo either fall back to traditional checkout or call show } ; notebranded samsung pay buttons can be found on the [samsung pay developers][8] portal under the resources tab direct cdn links will be available soon step 6 call the show method to display the payment sheet the payment sheet can be activated by calling its show method this method invokes the browser’s native ui so the user can examine the details of the purchase, add or change the information, and submit it for payment a promise, indicated by its then method and callback function, resolves what will be returned when the user accepts or rejects the payment request example – show request show then function paymentresponse { // process paymentresponse here paymentresponse complete "success" ; } catch function err { console error "uh oh, something bad happened", err message ; } ; step 7 handle the paymentresponse once the user approves the payment request by verifying the payment option and shipping option if provided , the show method’s promise resolves, resulting in a paymentresponse object comprised of the following fields methodname string indicating the chose payment method e g , visa details dictionary containing information for methodname shippingaddress shipping address of the user, if requested shippingoption id of the selected shipping option, if requested payeremail email address of the payer, if requested payerphone telephone number of the payer, if requested payername name of the payer, if requested here, it’s important to remember that the response from the payment request api must be submitted by the merchant in accordance with the pg’s integration model and apis in all cases, the samsung pay response is encapsulated within the paymentresponse details parameter, which comprises the following fields paymentcredential – contains the payment credential information necessary for processing the transaction with the pg in network token mode, this field includes 3ds data in gateway token mode, it includes token information network token mode type use “s” version 1 0 0; standard for payment authentication aka mastercard securecode, verified by visa, and american express safekey data encrypted payload value gateway token mode reference token id reference status authorized or rejected/declined billingaddress – contains the billing address and related attributes for the cardholder, possibly including country [iso3166] alpha-2 code; canonical form is upper case for example, “us” addressline[n] most specific part of the address; can include a street name, a house number, apartment number, a rural delivery route, descriptive instructions, or a post office box number region top level administrative subdivision of the country; can be a state, a province, an oblast, or a prefecture city city/town portion of the address dependentlocality dependent locality or sub-locality within a city; fused for neighborhoods, boroughs, districts, or uk dependent localities postalcode postal code or zip code, also known as pin code in india sortingcode bank sorting code; for example, in the british and irish banking industries, the sort code is a six-digit number, is usually formatted as three pairs of numbers, for example 12-34-56, identifying both the bank and the branch where the account is held languagecode [bcp47] language tag for the address, in canonical form; used to determine the field separators and the order of fields when formatting the address for display organization organization, firm, company, or institution at this address recipient name of the recipient or contact person this member may, under certain circumstances, contain multiline information; for example, it might contain “care of” information phone telephone number of the recipient or contact person paymentinfo – contains the payment information, including card_last4digits last four digits of the card’s dpan cardbrand currently, either mastercard or visa ordernumber merchant’s unique external reference id supplied in the original request’s methoddata parameter if the user pays with a credit card using the basic-card method, then the details response returned directly to your website from the browser will contain cardholdername, cardnumber, expirymonth, expiryyear, cardsecuritycode, billingaddress example request show then paymentresponse => { var paymentdata = { // payment method string, e g “amex” method paymentresponse methodname, // payment details contains payment information details paymentresponse details /* request details depends on pg token mode network - e g , first data; or gateway - e g , stripe ---------------------------------------------------------|------------------------------------------------------| * gateway token mode |* network token mode | * “details” { |* “details { | * “paymentcredential” { |* “method” “3ds”, | * “reference” “tok_1asceoyf6ypzj7f8se6grp0i”, |* “paymentcredential” { | * “status” “authorized” |* “type” “s”, | * }, |* “version” “100”, | * |* “data” “long_encrypted_payload_value”, | * |* }, | *--------------------------------------------------------|------------------------------------------------------| * “paymentinfo” { * “card_last4digits” “1489”, * “cardbrand” “mastercard”, * “ordernumber” “1233123”, * “billingaddress” { * “country” “usa”, * “addressline” [“chhccy”, “hdyxych”], * “region” “ca”, * “city” “mountain view”, * “dependentlocality” “”, * “postalcode” “94043”, * “sortingcode” “”, * “languagecode” “en”, * “organization” “”, * “recipient” “”, * “phone” “” * } * } * } * */ }; return fetch ‘/validatepayment’, { method ‘post’, headers { ‘content-type’ ‘application/json’ }, body json stringify paymentdata } then res => { if res status === 200 { return res json ; } else { throw ‘payment error’; } } then res => { paymentresponse complete “success” ; }, err => { paymentresponse complete “fail” ; } ; } catch err => { console error “error, something went wrong”, err message ; } ; once payment information is received from samsung pay, the website should submit the payment information to the merchant’s pg for transaction processing the ui will show a spinner while the request takes place when a response is received, the website should call complete to close the ui the website is then free to show an order complete or order confirmation page for user feedback as previously mentioned, you can simplify integration of network tokens by passing the encrypted payload directly to your pg and letting it handle decryption in all cases, how you handle a submitted network token depends on the payment gateway refer to your particular pg’s documentation for details paymentoptions is an optional parameter in thepaymentrequest constructor depending on your particular requirements, you may want additional information, such as the user’s shipping address for physical goods purchased and contact details for guest users paymentoptions currently comprises the following requestpayername true if payer name is required; otherwise, false requestpayeremail true if payer email address is required; otherwise, false requestpayerphone true if payer telephone number is required; otherwise, false requestshipping true if shipping address is required; otherwise, false shippingtype available label options “shipping/pickup/delivery” for indicating to user; solely for display purposes example var options = { requestpayeremail false, requestpayername true, requestpayerphone false, requestshipping true, shippingtype "delivery" } noteif any of the elements listed above are set to true, the browser payment sheet is launched; otherwise, the samsung pay payment sheet is displayed putting it all together let’s assemble the various code blocks into a prototype to demonstrate the w3c payment request api in action function onbuyclicked { const samsung_pay = 'https //spay samsung com'; if !window paymentrequest { // paymentrequest api is not available - forwarding to legacy form based experience location href = '/checkout'; } // setup var supportedinstruments = [{ supportedmethods [ samsung_pay ], // 'https //spay samsung com' data { "version" "1", "productid" "2bc3e6da781e4e458b18bc", //service id from partner portal "allowedcardnetworks" ['mastercard','visa'], "ordernumber" "1233123", "merchantname" "shop samsung demo ", //merchant name in partner portal "merchantgatewayparameter" {"userid" "acct_17irf7f6ypzj7wor"}, "isrecurring" false, "billingaddressrequired" false, "paymentprotocol" "protocol_3ds" } }]; var details = { displayitems [{ label 'original donation amount', amount { currency 'usd', value '65 00' } }, { label 'friends and family discount', amount { currency 'usd', value '-10 00' } }], total { label 'total due', amount { currency 'usd', value '55 00' } }; var options = { requestshipping true, requestpayeremail true, requestpayerphone true, requestpayername true }; // initialization var request = new paymentrequest supportedinstruments, details, options ; // when user selects a shipping address request addeventlistener 'shippingaddresschange', e => { e updatewith details, addr => { var shippingoption = { id '', label '', amount { currency ‘usd’, value ‘0 00’ }, selected true}; // shipping to us is supported if addr country === 'us' { shippingoption id = 'us'; shippingoption label = 'standard shipping in us'; shippingoption amount value = '0 00'; details total amount value = '55 00'; // shipping to jp is supported } else if addr country === 'jp' { shippingoption id = 'jp'; shippingoption label = 'international shipping'; shippingoption amount value = '10 00'; details total amount value = '65 00'; // shipping to elsewhere is unsupported } else { // empty array indicates rejection of the address details shippingoptions = []; return promise resolve details ; { // hardcoded for simplicity if details displayitems length === 2 { details displayitems[2] = shippingoption; } else { details displayitems push shippingoption ; } details shippingoptions = [shippingoption]; return promise resolve details ; } details, request shippingaddress ; } ; // when user selects a shipping option request addeventlistener 'shippingoptionchange', e => { e updatewith details => { // there should be only one option do nothing return promise resolve details ; } details ; } ; // show ui then continue with user payment info request show then result => { // post the result to the server return fetch '/pay', { method 'post', credentials ‘include’, headers { 'content-type' 'application/json' }, body json stringify result tojson } then res => { // only if successful if res status === 200 { return res json ; } else { throw 'failure'; } } then response => { // you should have received a json object if response success == true { return result complete 'success' ; } else { return result complete 'fail' ; } } then => { console log 'thank you!', result shippingaddress tojson , result methodname, result details tojson ; } catch => { return result complete 'fail' ; } ; } catch function err { console error 'uh oh, something bad happened ' + err message ; } ; } document queryselector '#start' addeventlistener 'click', onbuyclicked ; refer to the official w3c integration specs for additional details and definition testing once you have the code saved and loaded, you’re ready to test be sure to test your website or test domain on a device running samsung internet browser and with the samsung pay wallet app already set up and ready to go in accordance with the prerequisites cited above if you use a separate subdomain for your test environment, be sure to add it as an eligible service domain for the service you configured under registering your domain for the w3c service remember that any/all subdomains for production must also be added to the service, up to a maximum of 10 service domains again, please note that samsung pay’s webpay api currently supports mastercard and visa only support for american express and discover is under development and will be available soon be sure to test using the samsung internet and chrome mobile browser apps to test select samsung pay as the payment method by clicking on the branded samsung pay button branded implementations or the samsung pay radio button standard w3c implementations this should launch the samsung pay payment sheet authenticate payment validate your results end to end isolate issues in the log, debug, and test again contact your samsung pay rm to coordinate assistance with troubleshooting recommended test cases check if the samsung pay is available in the payment option on the website verify samsung pay logo on browser sheet verify order summary on the payment sheet verify “edit” and “pay” buttons on payment sheet verify purchased item in the summary verify the billing address on the payment sheet change the billing address try to change the card in payment sheet when only one card is enrolled in samsung pay try to change the card when multiple cards is enrolled in samsung pay verify the payment amount verify the payment options verify the payment completion screen verify merchant name on the payment sheet verify merchant domain name on the payments sheet verify “cancel” and “pay” buttons on browser sheet try to make payment with large amount larger than max allowed amount and verify the behavior make a payment using samsung pay with a card already added for the merchant’s website basic-card verify transaction notification after w3c purchase verify transaction notification after refund set payment options for shipping address and verify that browser payment sheet launches and captures shipping address input/changes by user verify that the shipping cost is updated based on a shipping address change and is reflected in the updated total amount release once your tests are successful and you are satisfied with the results, take the following steps to ready your integrated with samsung pay website for release go to the samsung pay developers portaland create a new release service sharing identical attributes with the service you successfully tested a click on service management, then click create new service b select for release pictured , then click next c select w3c mobile web payments as the service type, then click next d enter a "release" service name and select "united states" as the service country e select united states as the service country f select your payment gateway from the drop-down menu, then click connect with for gateway token mode or provide a valid csr for network token mode g enter your service domain and click add for each additional domain name, click add remember, when entering domain names on the portal, do not include a "https"//" prefix h confirm your agreement with the terms and conditions, then click next retrieve the service id from the service details page and enter copy-paste it into your website's methoddata object in place of the current testing service id when your service is approved by your samsung pay rm — as indicated in the status column of your service management dashboard — you're ready to release your integrated website to the public send queries concerning service package approval to webpayment@samsungpay com
Develop Samsung Pay
doc3 3 web checkout sdk 3 3 1 overview samsung pay web checkout enables seamless, secure payments on your website using cards stored in the samsung wallet app this javascript-based sdk makes it easy to integrate samsung pay into your desktop or mobile web checkout experience key features cross-device supportusers can complete purchases on both desktop and mobile browsers samsung wallet integrationpayments are authorized using cards saved in the samsung wallet mobile app secure credential transmissionpayment credentials are securely generated on the mobile device and transmitted to your website multiple authentication optionsusers can bind their device by either entering their samsung account email scanning a qr code displayed on your checkout page user scenario with the service flow the following figures describe the user scenario for making a purchase through samsung pay web checkout payment initiation & device binding the user selects samsung pay as the payment method at checkout a web checkout ui launches, prompting the user to link their device by either enter samsung account email scan a qr code using their mobile device a push notification is sent to their samsung wallet app for mobile devices the user selects samsung pay as the payment method at checkout a payment request pop-up is displayed and prompts the user to select the “pay” button the samsung wallet app automatically opens on the current device user confirmation on mobile device the user taps the notification on their device the samsung wallet app opens a payment sheet showing order details the user selects a payment card and authorizes the purchase payment completion a "verified" screen is shown in the browser as the transaction is confirmed your website receives a secure payment credential from samsung pay you forward this credential to your payment processor to complete the purchase 3 3 2 web checkout integration samsung pay web checkout enables seamless online payments using samsung wallet on supported mobile devices let’s us look how to integrate the web checkout sdk into your website and process secure, tokenized transactions prerequisites before integrating samsung pay web checkout, ensure the following samsung pay merchant id you must complete the partner onboarding process to obtain a valid merchant id tokenization support your acquirer and issuer must support tokenized in-app transactions per card network standards web checkout integration steps to integrate the samsung pay web checkout solution to your website include the samsung pay sdk add the sdk to your website's frontend <script src="https //img mpay samsung com/gsmpi/sdk/samsungpay_web_sdk js"></script> configure payment methods define the supported card brands, protocol, api version, and your service merchant id const paymentmethods = { "version" "2", "serviceid" "dcc1cbb25d6a470bb42926", "protocol" "protocol_3ds", "allowedbrands" ["visa","mastercard"] } initialize the samsung pay client set the environment "stage" – testing with device "stage_without_apk" – testing without device simulated "production" – live environment const samsungpayclient = new samsungpay paymentclient {environment "stage"} ; note 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 availability verify samsung pay availability in the user’s browser/device samsungpayclient isreadytopay paymentmethods then function response { if response result { // add a payment button } } catch function err { console error err ; } ; add samsung pay button use the official samsung pay button asset and adhere to branding guidelines <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> note download the official samsung pay button image and branding guideline from download page and use it directly in your html as shown here download attach click handler add your event handler to the button document getelementbyid "samsung-pay-btn" addeventlistener "click", onsamsungpaybuttonclicked ; create the transaction detail define transaction metadata such as order info, merchant details, and total amount 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 payment flow trigger the web checkout interface when the user clicks the payment button 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 note 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 paymentcredential => { // forward paymentcredential to your payment provider const paymentresult = { const paymentresult = { "status" "charged", "provider" "pg name" } samsungpayclient notify paymentresult ; } catch error => { payment credential sample the paymentcredential is the resulting output of the loadpaymentsheet method sample paymentcredential json output using jwe-only { "method" "3ds", "recurring_payment" false, "card_brand" "visa", "card_last4digits" "8226", "3ds" { "type" "s", "version" "100", "data" "eyjhbgcioijsu0exxzuilcjrawqioiixzhlsbkfvrvjttk53z0j0mmvzcevwu1poswrzzghqbvi3bzhqcdvkagvbpsisinr5cci6ikppu0uilcjjagfubmvsu2vjdxjpdhldb250zxh0ijoiulnbx1blssisimvuyyi6ikexmjhhq00ifq jykxn2h9pk1uj-4knpuij1r49ykw7-3aelznhadzsztclvjlhoyjomujfl1h21yq_5rmdwz9lj6o67j8m6kn_1dnkvnqaugi203ol5tegf-j15n_pcinj1nycfyivohazidbg9fq2nzts_muu9cvykiz-ifsuz6rfl9aiuoakjpctzpn8lwlddzxzme3j86sd45i-ahxwbujfvy9d2zrt1sddgoxgorjrzy3o5s29pybkaytjmcpc_jicu-sdsx3s1snm_cvhaqiccoxyidih6hfwo35fsswysvxu8yfpgtwbcdai9ujkptvr7npnp1ch85ja3dvw3mi87v-pwiqmw hdzesnbxu0d0t68e pcv1csibw7jgtlgfoovmebm-wggpw9rhonbkdb_qwwfl_cuf7_0nj_knuozq4pudk0_vzktbhi3kv0gt2ybmqs6zfpnxd3cdpgk_lyio8z8xciasoz5vltamjg7n5maadxxpvqwtcpk_tbksve2ke8w7r3u4kapfjl2ene06j3e4rkae367x8_aoxy2l3lhoeqzl4lfsntfs71xfc-s9h5-bgi2clkba-9hlrtpbxtumwa830rwywm7m fs5-tfbxq73l7icrrwkbla" } } the decrypted output will be similar to this { "amount" "100", "currency_code" "usd", "utc" "1719388643614", "eci_indicator" "5", "tokenpan" "5185731679991253", "tokenpanexpiration" "0127", "cryptogram" "akkeavcvwhfmammud6r3aoacfa==" } note for information about the content of the paymentmethods, transactiondetail, and paymentcredential data structures, see the api reference 3 3 3 decrypting payment credentials for security, samsung pay encrypts the payment credential using json web encryption jwe you must decrypt this payload to extract the payment token and process the transaction 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 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 '/', '_' ; } 3 3 4 integration on webview configure webview enablements to invoke samsung pay application in webview, you should override the shouldoverrideurlloading method javascript and dom storage are disabled in a webview by default you can enable through the websettings attached to your webview websettings allows any website to use javascript and dom storage for more information, visit websettings sample code kotlin import android webkit webview import android webkit webviewclient import android content intent import android content activitynotfoundexception companion object { private const val samsung_pay_url_prefix string = "samsungpay" private const val samsung_app_store_url string = "samsungapps //productdetail/com samsung android spay" } private lateinit var webview webview webview settings run { javascriptenabled = true domstorageenabled = true } webview webviewclient = object webviewclient { override fun shouldoverrideurlloading view webview, request webresourcerequest boolean { // get url from webresourcerequest val url = request url tostring // add below if statement to check if url is samsung pay or samsung app store deep link if url startswith samsung_pay_url_prefix || url startswith samsung_app_store_url , ignorecase = false { try { val intent = intent parseuri url, intent uri_intent_scheme startactivity intent } catch e activitynotfoundexception { // exception would be occured if the samsung wallet app is not installed // go to install samsung wallet app from market val installintent = intent parseuri "samsungapps //productdetail/com samsung android spay", intent uri_intent_scheme installintent addflags intent flag_activity_new_task startactivity installintent } // return true will cause that the url will not be loaded in webview return true } // the remaining part of the shouldoverrideurlloading method code // return false when you want to load url automatically by webview return false } } 3 3 5 sample implementation the following sample code implements the samsung pay web checkout button on a merchant site the implementation steps are described in web checkout integration for information about the content of the paymentmethods, transactiondetail, and paymentcredential data structures, see the api reference <!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <script src="https //img mpay samsung com/gsmpi/sdk/samsungpay_web_sdk js"></script> </head> <body> <div id="samsungpay-container"></div> <script> const samsungpayclient = new samsungpay paymentclient {environment "stage"} ; let paymentmethods = { version "2", serviceid "dcc1cbb25d6a470bb42926", protocol "protocol_3ds", allowedbrands ["visa","mastercard"] } samsungpayclient isreadytopay paymentmethods then function response { if response result { createandaddbutton ; } } catch function err { console error err ; } ; function createandaddbutton { const samsungpaybutton = samsungpayclient createbutton { onclick onsamsungpaybuttonclicked, buttonstyle "black", type "buy" } ; document getelementbyid "samsungpay-container" appendchild samsungpaybutton ; } function onsamsungpaybuttonclicked { let transactiondetail = { ordernumber "dstrf345789dsgty", merchant { name "virtual shop", url "virtualshop com", id "xn7qfnd", countrycode "us" }, amount { option "format_total_estimated_amount", currency "usd", total 300 } } samsungpayclient loadpaymentsheet paymentmethods, transactiondetail then function paymentcredential { console log "paymentcredential ", paymentcredential ; const paymentresult = { "status" "charged", "provider" "pg name" } samsungpayclient notify paymentresult ; } catch function error { console log "error ", error ; } ; } </script> </body> </html> 3 3 6 api reference let us learn the description of data structures used in the samsung pay web checkout api integration paymentmethods the paymentmethods object defines the payment methods that the merchant supports "paymentmethods" data structure elements key type required description version string required samsung pay api versionthe supported value is 2 serviceid string required merchant id that is assigned after onboarding protocol string required payment protocol typethe supported value is protocol_3ds allowedbrands list<string> required list of supported card brandsthe possible values are visamastercardamexdiscoverelomadacbjaywan tbd isrecurring boolean optional value if payment is recurringthe default value is false isbillingaddressrequired boolean optional value if billing address must be included in the payment credentials the default value is false iscardholdernamerequired boolean optional value if cardholder name must be included in the payment credentials the default value is false iscpfcardrequired boolean optional value if cpf must be included in the payment credentials the default value is false merchantchoicebrands object optional data structure containing configuration information for a co-badged card merchantchoicebrands type string required co-badged card display option for the payment sheetthe possible values are mandatory = only the brand defined in merchantchoicebrands brands is enabledpreference = the brand defined in merchantchoicebrands brands is selected by default but the user can change it merchantchoicebrands brands list<string> required list of supported brands for the co-badged cardthe possible values are madacb extrapaymentinfo object optional data structure containing additional supported features extrapaymentinfo id string required feature id for the additional featurethe possible values are combocard = combo carddsrp = digital secure remote payment extrapaymentinfo type string optional feature type, if the value of extrapaymentinfo id is dsrpthe possible values are ucaf = universal cardholder authentication fieldicc = integrated circuit cardthe default value is ucaf transactiondetail the transactiondetail object contains the transaction information for the user's purchase "transactiondetail" data structure elements key type required description ordernumber string required order number of the transactionthe following characters are allowed [a-z][a-z][0-9,-] merchant object required data structure containing merchant information merchant name string required merchant name merchant url string required merchant domain urlthe maximum length is 100 characters merchant id string conditional a unique identifier, known as the merchant unique id, is assigned by either merchant or the payment gateway pg or payment orchestrator po when a merchant is onboarded into their system this id is required in specific scenarios, namely when onboarding as a pg or po with samsung, or if the token brand is "mada" or the merchantchoicebrands brands includes "mada" the character limit for this id varies 15 characters for "mada" token brands and 45 characters for all other cases merchant countrycode string required merchant country codeiso-3166-1 alpha-2 amount object required data structure containing the payment amount amount option string required display format for the total amount on the payment sheetthe possible values are format_total_estimated_amount = display "total estimated amount " and total amountformat_total_price_only = display the total amount only amount currency string required currency codethe maximum length is 3 characters amount total string required total payment amount in the currency specified by amount currencythe amount must be an integer for example, 300 or in a format valid for the currency such as 2 decimal places after a separator, for example, 300 50 type string optional transaction typethis value is specifically supported for mada tokens and will not apply to other token types the possible values are purchasepreauthorizationthe default value is purchase paymentcredential the paymentcredential object contains the payment credential information generated by the samsung wallet application on the user's mobile device paymentcredential data structure elements key type required description card_brand string required brand of the payment card card_last4digit object required last 4 digits of the card number 3ds object required data structure containing the generated 3ds data 3ds type string optional 3ds typethe value is s for samsung pay 3ds version string required 3ds versionthe value for the current version is 100 3ds data string required encrypted payment credential data recurring_payment boolean required value if credential is enabled for recurringthe default value is false encryptedmessage string conditional encrypted string jwe that contains billing address, cardholder name and cpf when required by partner it can be decrypted in the same way as payment credentials encryptedmessage the decrypted encryptedmessage object in paymentcredential object contains billing address, cardholder name and cpf when required by partner "encryptedmessage" data structure elements key type required description billingaddress object conditional billing address billingaddress addressline1 string required address line 1 billingaddress addressline2 string optional address line 2 billingaddress city string required city billingaddress state string conditional state billingaddress countrycode string required country code iso 3166-1 alpha-3 billingaddress postalcode string required postal code cardholdername string conditional cardholder name cpf object conditional brazilian cpf cpf name string required the full name of the individual associated with the cpf cpf number string required the brazilian taxpayer number cpf , consisting of exactly 11 digits, without hyphens or dots paymentresult the paymentresult object contains the payment result information during transaction processing, and after the payment is processed with pg network paymentresult data structure elements key type required description status string required payment statusthe possible values are charged = payment was charge successfullycanceled = payment was canceled by either user, merchant, or acquirerrejected = payment was rejected by acquirererred = an error occurred during the payment process provider string optional payment provider pg name 3 3 7 partner checklist checklist for samsung pay web checkout on the merchant website, verify if the following functions works as expected samsung pay is available in the payment options section of the website samsung pay logo is displayed correctly in the payment options section after the samsung pay payment option is selected, the account/scan qr and email input options are displayed, and redirects the user to the samsung wallet app on their mobile device for the account option, “request to pay” and “cancel” buttons are displayed for the email option, “next” and “cancel” buttons, and a way to reset id are displayed for the scan qr option, the request automatically times out if you wait for more than 5 minutes, and you are redirected to the checkout screen once redirected to the samsung wallet app, “pay” and “cancel” buttons are displayed on a mobile browser, after the samsung pay payment option is selected, “continue with samsung pay” button is displayed samsung checkout screen is displayed the merchant domain name is displayed the order summary which contains the amount due, and product name is displayed the payment method selected is “samsung wallet” the contact information displays the customer’s name, phone, and email you should be able to modify this information, if needed “continue” and “cancel” buttons are displayed note these are relevant if you are executing an end-to-end test you can skip these tests if you are using a test transaction setup on the samsung wallet app via your test device, verify if the following functions works as expected a default card is displayed on the payment sheet the card name and last 4 digits of the card is displayed on the payment sheet you are able to change the card when multiple cards are enrolled in samsung pay if you requested for the transaction using billingaddress parameter, the billing address is displayed on the payment sheet the billing address can be filled and modified depending on the amount option parameter, the payment amount is displayed as “total” or “total estimated amount ” the merchant name is displayed on the payment sheet the pin/biometric authentication option is displayed to proceed with payment confirmation the “verified” checkmark is displayed in blue upon payment confirmation if you are testing with actual cards, and samsung wallet is in production environment, confirm the transaction notification on the mobile phone is displayed once the purchase is made on transaction completion, verify the following on the merchant website the payment completion screen is displayed on the mobile or non-mobile device, depending where the transaction is initiated you are able to initiate a payment using samsung pay with a card already added for the merchant’s website basic card
Develop Smart TV
docweb app html5 memory optimization guide building memory-efficient web apps on constrained tv hardware audience third-party developers building web apps html5 for tv purpose principles and practices for using memory efficiently on constrained tv hardware runtime a chromium-based web engine on the tv blink renderer + v8 javascript engine nature of this document this is a best-practice guide it is not intended to mandate compliance or to trigger any action based on compliance — use it as a reference for better app quality and user experience contents why memory matters for tv web apps understanding the memory model of web apps image and graphics memory optimization dom management and list/grid virtualization preventing memory leaks javascript memory and gc management video & media memory management data handling optimization fetch / json / cache canvas / webgl / web audio / worker cautions 1 why memory matters for tv web apps tvs have different memory constraints than smartphones or pcs understanding this difference is the starting point of optimization memory is a shared resource when one app uses too much memory, it isn't only that app that slows down — the whole tv degrades app switching gets slower, other apps in the background get killed, and system animations stutter core principle memory is managed, not borrowed make "allocate when needed, release the moment it's no longer needed" the default for every feature you design 2 understanding the memory model of web apps to optimize, you first need to know where a web app's memory goes unlike native apps, a web app's memory is spread across several layers, much of which is not directly visible to the developer 2 1 major memory-consuming areas area description developer control v8 js heap javascript objects, arrays, closures, functions, etc high dom / render tree dom nodes, render objects, computed style high decoded images not the compressed file, but the bitmap expanded for display high gpu memory textures/layers compositing layers, textures, canvas back buffer medium media buffers video/audio decode buffers, mse sourcebuffer medium network & cache http cache, fetch responses, string buffers medium engine overhead v8/blink internal structures, fonts, parsers, etc low 2 2 "compressed file size" is not "memory usage" this is one of the most important concepts for understanding memory usage even a 100 kb jpeg must be decompressed into a bitmap to be displayed most pixels take 4 bytes rgba so a single 1920×1080 image = about 8 3 mb of decode memory 1920 × 1080 × 4 ≈ 8,294,400 bytes a single 4k 3840×2160 image is about 33 mb "it's a small file, so it's fine" is a misconception the real memory is the number of pixels actually drawn on screen × 4 bytes the same applies to json when you json parse a 200 kb json string, the original string stays in memory while the parsed object tree is created on top of it the parse result can take several times more memory than the original 2 3 the v8 heap and garbage collection gc javascript is a gc language gc only reclaims objects that are no longer reachable their references are cut conversely, if a reference remains anywhere, the object is never reclaimed this is the root cause of web app memory leaks gc isn't free while gc runs, the main thread can briefly pause, which shows up as animation frame drops v8 uses a generational gc newly created objects are reclaimed cheaply in the young generation, so creating and discarding temporary objects in ordinary code is not itself a problem the place to watch is hot paths that run tens to hundreds of times per second animation loops, scroll/input handlers, tight loops creating a new object every time there makes even a cheap gc run frequently, causing frame jank so the fix is not to reduce allocations blindly, but to reuse objects only in hot paths see 6 1 the cause of a leak is not "circular references" per se, but "something is still referencing it, so gc can't reclaim it " javascript's gc reclaims even a cyclic structure a→b, b→a correctly once nothing outside references it; the same is true for cycles involving dom nodes real leaks happen when global variables, long-lived arrays/objects, closures, or unremoved event listeners keep holding an object see section 5 3 image and graphics memory optimization images are the single largest memory consumer in almost every tv web app just following the principles in this section resolves most memory problems avoid animated gifs — they use a lot of memory use css animations for simple ui motion, and if you truly need raster animation, use animated webp instead of gif 3 1 load images at the size you display most important the key is not to download an image larger than the size you display and shrink it on the device don't shrink a large original into a small area putting a 1920×1080 original into a 200×300 thumbnail slot looks small on screen, but you still download that large original and decode it at least once — wasting network, cpu, and peak memory download images resized on the server to the display size thumbnails at thumbnail size, backgrounds at background size don't count on the browser to shrink it for you the browser sometimes downscales an image to the display size, but not always, and the cost of downloading and expanding the large original is incurred regardless the reliable way to save memory is to receive an image that is already at the display size <!-- bad using a 4k original for a thumbnail --> <img src="poster_4k jpg" style="width 200px; height 300px"> <!-- → small on screen, but the 4k original is still downloaded and decoded wasted network / cpu / memory --> <!-- good serve an image sized for display from the server --> <img src="poster_200x300 jpg" width="200" height="300"> 3 2 don't load offscreen images; release them when not visible lazy loading don't load images not yet visible from scrolling load them only as they approach the viewport, via the loading="lazy" attribute or intersectionobserver <img src="poster jpg" loading="lazy" width="200" height="300" alt=" "> actively release images that leave the screen in a long tv grid, reclaim decode memory for images far from the viewport by clearing src or removing the element see section 4 on virtualization // release image memory for a card far from the viewport function releaseimage imgel { imgel removeattribute 'src' ; // or imgel src = ''; imgel removeattribute 'srcset' ; } applying content-visibility auto to large offscreen sections can defer their rendering/layout cost and associated memory offscreen-section { content-visibility auto; contain-intrinsic-size 400px 300px; /* size hint */ } 3 3 treat css background images the same way an image set via background-image also uses bitmap memory once decoded background images of hidden elements can still load, so make large backgrounds load only when shown — by attaching a class at that moment 3 4 control decode timing suddenly attaching a large image to the screen can trigger a synchronous decode on the main thread and cause jank use the decoding="async" attribute or the img decode promise to decode ahead of time, asynchronously, before display const img = new image ; img src = 'poster jpg'; await img decode ; // after decode completes container appendchild img ; // attach without a frame drop 3 5 watch out for web fonts web fonts can be as large a single consumer as images, yet are often overlooked the browser keeps a font's glyphs resident in memory, and receiving a separate file per weight/style multiplies that in particular, languages with thousands to tens of thousands of glyphs, such as cjk chinese, japanese, korean , can have a single full-glyph font file reaching several mb, so take special care first ask whether you even need a web font if the tv platform's built-in fonts are enough, not using a web font at all is the surest saving load only the weights/styles you need limit to regular/bold and don't download unused light, thin, italic, etc use subset fonts building a font file that contains only the characters you actually use greatly reduces resident memory the more glyphs a language has, the bigger the effect unicode-range prevents downloading unused ranges when you split into multiple files, but to reduce resident memory itself, the fundamental approach is to prepare a subset file that contains only the glyphs you need use font-display swap so font loading doesn't block text rendering better perceived performance to reduce transfer size, use the well-compressed woff2 /* apply a subset only the needed glyphs to a specific character range */ @font-face { font-family 'appfont'; src url 'app-latin-subset woff2' format 'woff2' ; unicode-range u+0000-00ff; /* basic latin only */ font-display swap; } 4 dom management and list/grid virtualization after images, the biggest driver of a tv web app's memory is the number of dom nodes tv apps are especially vulnerable because they typically have long grid/carousel structures with hundreds to thousands of content cards 4 1 every dom node is memory each dom element carries a node object, computed style, layout info, and if displayed a render layer if you put thousands of cards into the dom at once, all of them stay resident even when only a few are visible as the dom grows, style recalculation, layout, and compositing costs grow too, hurting performance 4 2 virtualize long lists windowing — do it virtualization is a must for long lists/grids keep only the visible items plus a small buffer actually in the dom, and recycle as the user scrolls key ideas render only the items visible in the viewport + a margin above and below, not the whole list remove from the dom or recycle elements that scroll out of view fake the full scroll height with a spacer element or a transform // conceptual example simple windowing with intersectionobserver const buffer = 5; // extra items above and below function renderwindow startindex, endindex, alldata, container { container textcontent = ''; // remove previous items → reclaim dom nodes & image memory const frag = document createdocumentfragment ; for let i = startindex - buffer; i <= endindex + buffer; i++ { if i < 0 || i >= alldata length continue; frag appendchild createcard alldata[i] ; } container appendchild frag ; } in a tv app, any list with more than ~100 items should almost always consider virtualization during validation, apps that "render thousands of cards as one big dom" are a classic memory-overrun case 4 3 recycle elements — the dom version of the object pool pattern same idea as the object pool 6 2 creating and discarding a card every time increases dom create/destroy and gc load instead, create a fixed number of dom nodes and just swap their content // create only as many card dom nodes as can be visible, then reuse them const pool = []; function getcard { return pool pop || createcard ; } function recycle card { card queryselector 'img' removeattribute 'src' ; // release image memory pool push card ; } 4 4 clean up the previous screen's dom on navigation in an spa, leaving the previous screen's dom in place keeps occupying memory on routing, remove the previous view's dom and release its listeners, timers, and observers together giving each screen component a paired mount and destroy , where destroy undoes everything it created listeners, timers, observers, dom , structurally prevents leaks see section 5 4 5 reduce excessive wrapper / shadow dom unnecessary abstraction layers increase node count cut meaningless <div> nesting and excessive wrapper components, and remove unneeded layers to keep the dom shallow and simple 4 6 beware compositing-layer gpu memory blowup separate from dom node count, the browser promotes some elements to their own compositing layers, managed as gpu textures each layer takes width × height × 4 bytes of gpu memory, by the same principle as a decoded image a single full-screen layer is about 8 mb fhd , so if layers grow to dozens or hundreds, a tv with tight gpu memory hits its limit quickly layers arise mainly from forced-promotion hints like will-change, transform translatez 0 / translate3d 0,0,0 , or from animated elements don't slap translatez 0 on every card on the belief that "promoting to a layer makes it faster " it instead wastes gpu memory and increases compositing cost turn will-change on only right before an animation and remove it when done leaving it on permanently keeps that many layers resident don't stack multiple full-screen layers front overlays, fade layers, etc use the devtools layers panel or the "layer borders" option to check the actual layer count and sizes rule promote layers "only when needed, only on the elements that need it " don't sprinkle hints out of habit 5 preventing memory leaks a memory leak is when an object that is no longer needed can't be reclaimed because something still references it, and it is fatal in long-running tv apps the patterns that cause leaks in web apps are well known 5 1 unremoved event listeners the most common cause a listener registered with addeventlistener keeps holding the target object and the callback and everything its closure captures unless you explicitly remove it in particular, listeners on long-lived objects like window and document stay until the app ends // bad the listener remains after the component is removed, holding the whole object graph window addeventlistener 'resize', this onresize ; // good 1 always remove in a matching pair window removeeventlistener 'resize', this onresize ; // good 2 clean up all at once with abortcontroller const controller = new abortcontroller ; window addeventlistener 'resize', onresize, { signal controller signal } ; element addeventlistener 'keydown', onkey, { signal controller signal } ; // on cleanup controller abort ; // removes every listener registered with this controller at once rule listeners you register when creating a component/screen must all be removed when destroying it making abortcontroller + signal your standard pattern reduces mistakes 5 2 reduce listeners with delegation in a tv grid with hundreds to thousands of cards, attaching a click/keydown listener to each card keeps as many callback closures resident as there are listeners, plus register/unregister cost every time a card is created or discarded instead, attach a single listener to a parent container and use event bubbling with event target to determine which card fired it event delegation the listener count drops to one, cutting both memory and cleanup burden // bad a listener per card → n callback closures resident cards foreach card => card addeventlistener 'click', onclick ; // good delegate to a single container → 1 listener grid addeventlistener 'click', e => { const card = e target closest ' card' ; if !card return; handleselect card dataset id ; } ; it pairs especially well with virtualization 4 2 and element recycling 4 3 even when you swap card dom, the container's listener stays, so you don't have to re-register a listener each time cleanup is also just removing the one container listener, which reduces the listener-leak risk from 5 1 5 3 unremoved timers and callbacks if setinterval, settimeout, or requestanimationframe reference objects in their callbacks, those objects stay alive as long as the timer does in particular, setinterval and a recursive requestanimationframe run forever unless you explicitly stop them const timerid = setinterval update, 1000 ; // cleanup clearinterval timerid ; let rafid = requestanimationframe loop ; // cleanup cancelanimationframe rafid ; always stop animation loops and polling timers when the screen goes to the background or is destroyed you can detect visibility with the visibilitychange event 5 4 detached dom nodes a node removed from the dom but still referenced by a javascript variable is not gc'd this is called "detached dom" and is a classic type of web app leak // bad const cache = {}; const list = document getelementbyid 'list' ; cache oldlist = list; // keeps a reference list remove ; // removed from the dom // removed from the dom, but cache oldlist still holds it, so it isn't reclaimed including all child nodes // good cache oldlist = null; // cut the reference so it can be reclaimed 5 5 global caches/arrays that grow without bound logs, event records, response caches, and so on grow without limit if you keep piling them into global arrays/objects always cap the size e g , lru or clear periodically because map preserves insertion order, deleting and re-inserting an item on each access keeps the "least recently used" item at the front, giving you a simple lru // a simple bounded lru cache const max = 50; const cache = new map ; function get key { if !cache has key return undefined; const value = cache get key ; cache delete key ; // delete, then cache set key, value ; // re-insert → most-recently-used moves to the back return value; } function put key, value { if cache has key cache delete key ; // reset order on update else if cache size >= max { cache delete cache keys next value ; // evict the least-recently-used front } cache set key, value ; } 5 6 large objects held by closures a closure captures variables from its outer scope a single callback can keep a huge data structure alive reference only the values you actually need inside the callback 5 7 use weakmap / weakref when you want to attach extra info to an object without affecting its lifetime, use weakmap when the key object disappears, the related entry is reclaimed automatically const metadata = new weakmap ; metadata set domnode, { lastfocused 0 } ; // when domnode is gc'd, this entry disappears too 5 8 release memory aggressively when backgrounded as seen in section 1, when memory is low the tv kills background apps if your app holds a lot of memory while off-screen the user switched to another app/input , it becomes the first to be killed and reloads from scratch on return, hurting ux so the moment your app becomes invisible, give back memory you don't currently need release offscreen images / decoded bitmaps 3 2 return videos/decoders not currently playing 7 1 clear caches you can rebuild 5 5 stop animation loops / polling timers 5 3 detect show/hide with visibilitychange document hidden release resources the moment the app is hidden, and restore only what's needed when it becomes visible again document addeventlistener 'visibilitychange', => { if document hidden { releaseoffscreenimages ; // release image bitmaps pauseandreleasevideos ; // return videos not currently playing stopanimationloops ; // stop raf / timers trimcaches ; // clear caches you can rebuild } else { restoreonresume ; // on return, re-prepare only what's needed } } ; rule preserve lightweight state such as scroll position and focus, but drop heavy resources bitmaps, buffers, caches in the background and rebuild them on return 5 9 how to diagnose leaks in chrome devtools' memory panel, take two or more heap snapshots and compare them comparison to find objects that keep growing filter by "detached" to directly inspect detached dom nodes open and close the same screen several times, then compare snapshots; objects that keep accumulating even after closing indicate a leak 6 javascript memory and gc management 6 1 suppress memory churn memory churn means creating and discarding many temporary objects in a short time this makes gc run often, leading to animation frame drops and performance degradation watch these places especially inside animation loops requestanimationframe inside scroll handlers and input remote-control key handlers inside frequently running for loops // bad a new object/array every frame function onframe { const pos = { x computex , y computey }; // new object every frame → gc pressure moveto pos ; requestanimationframe onframe ; } // good reuse the object const pos = { x 0, y 0 }; function onframe { pos x = computex ; pos y = computey ; moveto pos ; requestanimationframe onframe ; } to maintain 60 fps, the budget per frame is about 16 ms to stay within it, minimize allocations during the frame 6 2 object pool — use with care returning reusable objects particles, card view-models, etc to a pool instead of discarding them can reduce allocation/gc but a pool has its own management cost, and an oversized pool actually increases resident memory and gc load apply it only where allocation is a real bottleneck hot paths , after measuring 6 3 choose efficient data structures choosing the right data structure for the job saves memory for large numeric data, use a typedarray int32array, float32array, uint8array, etc instead of a plain array [] it stores the values themselves densely in contiguous memory without creating a separate object per element, so the difference is large compared with, for example, an array of {x, y} objects for frequent key-value lookups, map is more suitable and clearer than an object {} ; for membership only, use set when handling large arrays, don't overuse unnecessary copies slice, spread [ arr], concat each copy allocates new memory // bad a million coordinates as an array of objects const points = []; for let i = 0; i < 1_000_000; i++ points push { x 0, y 0 } ; // good a single typedarray x, y interleaved const points = new float32array 1_000_000 * 2 ; 6 4 watch strings when concatenating string fragments many times, collecting them in an array and joining once with join is safer for memory and performance don't keep a huge response string in its original form for long extract only what you need, then cut the reference to the original so it can be reclaimed 6 5 cut references explicitly assign null to a large object you no longer need to cut the reference so gc can reclaim it especially when the variable's scope is wide or long-lived this bigdata = null; // release the large data note to empty a value, obj prop = null is generally better than delete obj prop delete can break the engine's internal object optimizations and hurt performance 6 6 your app code bundle takes memory too so far we've covered data created at runtime images, dom, objects , but your app's javascript/css code itself uses memory v8 parses loaded scripts and keeps them resident, so the bigger the bundle, the higher the baseline footprint from startup the less code you load, the less resident memory don't include unnecessarily large libraries instead of pulling in a heavy dependency whole for one or two features, use only the parts you need, or consider a lighter alternative or your own implementation also avoid including duplicate libraries that do the same thing minify + tree-shaking at build time to remove unused dead code code-split to load only per-screen code don't ship every route's code on the first screen; split with dynamic import import at entry time not loading unused screens' code into memory at all is best excessive abstraction layers increase code size the same principle as 4 5 removing unnecessary dom wrappers applies to code 7 video & media memory management the core of a tv app is mostly video playback media uses a lot of memory, so managing it matters 7 1 always release media resources that finished/left playback if you only remove a <video> element from the screen and leave it, the decoder and buffers may remain release it definitively in this order function releasevideo video { video pause ; video removeattribute 'src' ; // remove the src attribute // if you used <source> child elements, remove those too video load ; // prompt release of internal buffers/decoder // if you used mse, also clean up sourcebuffer/mediasource } don't keep multiple <video> elements alive at once when implementing preview autoplay etc , release a preview video the moment it leaves the screen a tv has a limited number of hardware video decoders, and each decoder uses large buffers reusing a single <video> element is safer than creating and discarding one per content item 7 2 mse media source extensions buffer management if you implement adaptive streaming yourself, remove the buffer for already-played ranges with sourcebuffer remove otherwise the buffer keeps growing and eats memory cap the forward buffer prefetching minutes ahead is convenient but uses a lot of memory be conservative with buffer size, especially at 4k / high bitrate 7 3 subtitle / thumbnail tracks don't keep thumbnail-preview scrub-preview sprites fully resident; load/release only the ranges you need reclaim used subtitle tracks and blob urls with url revokeobjecturl a blob url is not released unless you explicitly revoke it const url = url createobjecturl blob ; // after use url revokeobjecturl url ; 8 data handling optimization fetch / json / cache the amount and handling of data fetched over the network directly affect memory usage 8 1 fetch only as much as you need server-side filtering & paging don't fetch thousands of list items at once; split them via paging / infinite scroll don't receive large responses containing fields the client won't use design the api so the server filters down to only the needed fields e g , graphql field selection, a rest fields= parameter fetching, parsing, and storing large data itself leads to excessive memory use, which in turn degrades performance 8 2 understand the cost of json parse json parse str turns a string into an object tree, taking memory for the parsed objects on top of the original string the larger the json, the more this double occupancy hurts don't keep holding the original string reference after parsing cut it to allow reclamation for very large datasets, consider streaming parsing or partial parsing avoid the pattern of parsing everything and holding it all 8 3 don't hold parse results whole for long rather than keeping the entire list api response in memory, transform it into the shape the screen needs a view-model and then discard the original response in infinite scroll, cap and clean up data for pages already passed far from the screen data, like the dom, is a target for "windowing " 8 4 efficient data formats for large, repetitive data, use a compact representation rather than a verbose format json is usually the standard on the web, but consider the following avoid unnecessarily nested or verbose json structures; keep key names and structure compact don't stuff binary data into json as base64 about 33% bloat ; receive it as a separate binary response or an arraybuffer if very large structured data is sent repeatedly, consider binary serialization e g , protobuf, cbor 8 5 cap your caches cap response caches, image caches, and computed-result caches with an lru limit so they don't grow without bound see 5 5 don't put large data in localstorage/sessionstorage; they have size limits and are synchronous apis bad for performance if you truly need large data, use indexeddb — and still manage a cap 9 canvas / webgl / web audio / worker cautions more powerful web apis can use a lot of memory and often hold resources gc won't reclaim automatically the following applies only if you use these apis directly — skip it if you don't 9 1 canvas 2d canvas back-buffer memory = width × height × 4 bytes don't create many large canvases for an unused canvas, shrink its size to width = height = 0 to reclaim the back buffer, and cut the reference creating a large array every frame with getimagedata/putimagedata causes memory churn reuse buffers 9 2 webgl webgl resources textures, buffers, programs, framebuffers are not reclaimed by gc automatically delete them explicitly deletetexture, deletebuffer, deleteprogram, deleteframebuffer when you no longer need the context, release it with getextension 'webgl_lose_context' losecontext textures use large gpu memory by the same principle as image bitmaps control texture size and count 9 3 web audio close an audiocontext with close after use don't leave many open a decoded audiobuffer can be large; cut the reference when done 9 4 web worker a worker has its own memory space terminate a finished worker with terminate ; leaving it keeps occupying memory passing large data to a worker copies it structured clone , creating two copies use a transferable e g , transferring ownership of an arraybuffer to avoid the copy worker postmessage buffer, [buffer] ; // transfer ownership of buffer → no copy 9 5 iframe each iframe loads its own document and resources, using a lot of memory create ad / external-widget iframes only when needed, and remove them from the dom to destroy them completely when done in closing tv web app memory optimization boils down to three principles create only as much as you show — images at display size, lists only as far as visible, data only as much as needed release the moment it's no longer needed — clean up listeners, timers, observers, media, dom, and caches along their lifecycle measure and verify on a real device — confirm memory doesn't trend upward across repeated open/close and long runs apply these three principles from the design stage, and you'll deliver a smooth, stable app experience to users this document is a draft and will be continually improved through review refer to separate platform documentation for platform-specific policies and support details ↑ back to top
Develop Smart Signage
docweb app html5 memory optimization guide building memory-efficient web apps on constrained tv hardware audience third-party developers building web apps html5 for tv purpose principles and practices for using memory efficiently on constrained tv hardware runtime a chromium-based web engine on the tv blink renderer + v8 javascript engine nature of this document this is a best-practice guide it is not intended to mandate compliance or to trigger any action based on compliance — use it as a reference for better app quality and user experience contents why memory matters for tv web apps understanding the memory model of web apps image and graphics memory optimization dom management and list/grid virtualization preventing memory leaks javascript memory and gc management video & media memory management data handling optimization fetch / json / cache canvas / webgl / web audio / worker cautions 1 why memory matters for tv web apps tvs have different memory constraints than smartphones or pcs understanding this difference is the starting point of optimization memory is a shared resource when one app uses too much memory, it isn't only that app that slows down — the whole tv degrades app switching gets slower, other apps in the background get killed, and system animations stutter core principle memory is managed, not borrowed make "allocate when needed, release the moment it's no longer needed" the default for every feature you design 2 understanding the memory model of web apps to optimize, you first need to know where a web app's memory goes unlike native apps, a web app's memory is spread across several layers, much of which is not directly visible to the developer 2 1 major memory-consuming areas area description developer control v8 js heap javascript objects, arrays, closures, functions, etc high dom / render tree dom nodes, render objects, computed style high decoded images not the compressed file, but the bitmap expanded for display high gpu memory textures/layers compositing layers, textures, canvas back buffer medium media buffers video/audio decode buffers, mse sourcebuffer medium network & cache http cache, fetch responses, string buffers medium engine overhead v8/blink internal structures, fonts, parsers, etc low 2 2 "compressed file size" is not "memory usage" this is one of the most important concepts for understanding memory usage even a 100 kb jpeg must be decompressed into a bitmap to be displayed most pixels take 4 bytes rgba so a single 1920×1080 image = about 8 3 mb of decode memory 1920 × 1080 × 4 ≈ 8,294,400 bytes a single 4k 3840×2160 image is about 33 mb "it's a small file, so it's fine" is a misconception the real memory is the number of pixels actually drawn on screen × 4 bytes the same applies to json when you json parse a 200 kb json string, the original string stays in memory while the parsed object tree is created on top of it the parse result can take several times more memory than the original 2 3 the v8 heap and garbage collection gc javascript is a gc language gc only reclaims objects that are no longer reachable their references are cut conversely, if a reference remains anywhere, the object is never reclaimed this is the root cause of web app memory leaks gc isn't free while gc runs, the main thread can briefly pause, which shows up as animation frame drops v8 uses a generational gc newly created objects are reclaimed cheaply in the young generation, so creating and discarding temporary objects in ordinary code is not itself a problem the place to watch is hot paths that run tens to hundreds of times per second animation loops, scroll/input handlers, tight loops creating a new object every time there makes even a cheap gc run frequently, causing frame jank so the fix is not to reduce allocations blindly, but to reuse objects only in hot paths see 6 1 the cause of a leak is not "circular references" per se, but "something is still referencing it, so gc can't reclaim it " javascript's gc reclaims even a cyclic structure a→b, b→a correctly once nothing outside references it; the same is true for cycles involving dom nodes real leaks happen when global variables, long-lived arrays/objects, closures, or unremoved event listeners keep holding an object see section 5 3 image and graphics memory optimization images are the single largest memory consumer in almost every tv web app just following the principles in this section resolves most memory problems avoid animated gifs — they use a lot of memory use css animations for simple ui motion, and if you truly need raster animation, use animated webp instead of gif 3 1 load images at the size you display most important the key is not to download an image larger than the size you display and shrink it on the device don't shrink a large original into a small area putting a 1920×1080 original into a 200×300 thumbnail slot looks small on screen, but you still download that large original and decode it at least once — wasting network, cpu, and peak memory download images resized on the server to the display size thumbnails at thumbnail size, backgrounds at background size don't count on the browser to shrink it for you the browser sometimes downscales an image to the display size, but not always, and the cost of downloading and expanding the large original is incurred regardless the reliable way to save memory is to receive an image that is already at the display size <!-- bad using a 4k original for a thumbnail --> <img src="poster_4k jpg" style="width 200px; height 300px"> <!-- → small on screen, but the 4k original is still downloaded and decoded wasted network / cpu / memory --> <!-- good serve an image sized for display from the server --> <img src="poster_200x300 jpg" width="200" height="300"> 3 2 don't load offscreen images; release them when not visible lazy loading don't load images not yet visible from scrolling load them only as they approach the viewport, via the loading="lazy" attribute or intersectionobserver <img src="poster jpg" loading="lazy" width="200" height="300" alt=" "> actively release images that leave the screen in a long tv grid, reclaim decode memory for images far from the viewport by clearing src or removing the element see section 4 on virtualization // release image memory for a card far from the viewport function releaseimage imgel { imgel removeattribute 'src' ; // or imgel src = ''; imgel removeattribute 'srcset' ; } applying content-visibility auto to large offscreen sections can defer their rendering/layout cost and associated memory offscreen-section { content-visibility auto; contain-intrinsic-size 400px 300px; /* size hint */ } 3 3 treat css background images the same way an image set via background-image also uses bitmap memory once decoded background images of hidden elements can still load, so make large backgrounds load only when shown — by attaching a class at that moment 3 4 control decode timing suddenly attaching a large image to the screen can trigger a synchronous decode on the main thread and cause jank use the decoding="async" attribute or the img decode promise to decode ahead of time, asynchronously, before display const img = new image ; img src = 'poster jpg'; await img decode ; // after decode completes container appendchild img ; // attach without a frame drop 3 5 watch out for web fonts web fonts can be as large a single consumer as images, yet are often overlooked the browser keeps a font's glyphs resident in memory, and receiving a separate file per weight/style multiplies that in particular, languages with thousands to tens of thousands of glyphs, such as cjk chinese, japanese, korean , can have a single full-glyph font file reaching several mb, so take special care first ask whether you even need a web font if the tv platform's built-in fonts are enough, not using a web font at all is the surest saving load only the weights/styles you need limit to regular/bold and don't download unused light, thin, italic, etc use subset fonts building a font file that contains only the characters you actually use greatly reduces resident memory the more glyphs a language has, the bigger the effect unicode-range prevents downloading unused ranges when you split into multiple files, but to reduce resident memory itself, the fundamental approach is to prepare a subset file that contains only the glyphs you need use font-display swap so font loading doesn't block text rendering better perceived performance to reduce transfer size, use the well-compressed woff2 /* apply a subset only the needed glyphs to a specific character range */ @font-face { font-family 'appfont'; src url 'app-latin-subset woff2' format 'woff2' ; unicode-range u+0000-00ff; /* basic latin only */ font-display swap; } 4 dom management and list/grid virtualization after images, the biggest driver of a tv web app's memory is the number of dom nodes tv apps are especially vulnerable because they typically have long grid/carousel structures with hundreds to thousands of content cards 4 1 every dom node is memory each dom element carries a node object, computed style, layout info, and if displayed a render layer if you put thousands of cards into the dom at once, all of them stay resident even when only a few are visible as the dom grows, style recalculation, layout, and compositing costs grow too, hurting performance 4 2 virtualize long lists windowing — do it virtualization is a must for long lists/grids keep only the visible items plus a small buffer actually in the dom, and recycle as the user scrolls key ideas render only the items visible in the viewport + a margin above and below, not the whole list remove from the dom or recycle elements that scroll out of view fake the full scroll height with a spacer element or a transform // conceptual example simple windowing with intersectionobserver const buffer = 5; // extra items above and below function renderwindow startindex, endindex, alldata, container { container textcontent = ''; // remove previous items → reclaim dom nodes & image memory const frag = document createdocumentfragment ; for let i = startindex - buffer; i <= endindex + buffer; i++ { if i < 0 || i >= alldata length continue; frag appendchild createcard alldata[i] ; } container appendchild frag ; } in a tv app, any list with more than ~100 items should almost always consider virtualization during validation, apps that "render thousands of cards as one big dom" are a classic memory-overrun case 4 3 recycle elements — the dom version of the object pool pattern same idea as the object pool 6 2 creating and discarding a card every time increases dom create/destroy and gc load instead, create a fixed number of dom nodes and just swap their content // create only as many card dom nodes as can be visible, then reuse them const pool = []; function getcard { return pool pop || createcard ; } function recycle card { card queryselector 'img' removeattribute 'src' ; // release image memory pool push card ; } 4 4 clean up the previous screen's dom on navigation in an spa, leaving the previous screen's dom in place keeps occupying memory on routing, remove the previous view's dom and release its listeners, timers, and observers together giving each screen component a paired mount and destroy , where destroy undoes everything it created listeners, timers, observers, dom , structurally prevents leaks see section 5 4 5 reduce excessive wrapper / shadow dom unnecessary abstraction layers increase node count cut meaningless <div> nesting and excessive wrapper components, and remove unneeded layers to keep the dom shallow and simple 4 6 beware compositing-layer gpu memory blowup separate from dom node count, the browser promotes some elements to their own compositing layers, managed as gpu textures each layer takes width × height × 4 bytes of gpu memory, by the same principle as a decoded image a single full-screen layer is about 8 mb fhd , so if layers grow to dozens or hundreds, a tv with tight gpu memory hits its limit quickly layers arise mainly from forced-promotion hints like will-change, transform translatez 0 / translate3d 0,0,0 , or from animated elements don't slap translatez 0 on every card on the belief that "promoting to a layer makes it faster " it instead wastes gpu memory and increases compositing cost turn will-change on only right before an animation and remove it when done leaving it on permanently keeps that many layers resident don't stack multiple full-screen layers front overlays, fade layers, etc use the devtools layers panel or the "layer borders" option to check the actual layer count and sizes rule promote layers "only when needed, only on the elements that need it " don't sprinkle hints out of habit 5 preventing memory leaks a memory leak is when an object that is no longer needed can't be reclaimed because something still references it, and it is fatal in long-running tv apps the patterns that cause leaks in web apps are well known 5 1 unremoved event listeners the most common cause a listener registered with addeventlistener keeps holding the target object and the callback and everything its closure captures unless you explicitly remove it in particular, listeners on long-lived objects like window and document stay until the app ends // bad the listener remains after the component is removed, holding the whole object graph window addeventlistener 'resize', this onresize ; // good 1 always remove in a matching pair window removeeventlistener 'resize', this onresize ; // good 2 clean up all at once with abortcontroller const controller = new abortcontroller ; window addeventlistener 'resize', onresize, { signal controller signal } ; element addeventlistener 'keydown', onkey, { signal controller signal } ; // on cleanup controller abort ; // removes every listener registered with this controller at once rule listeners you register when creating a component/screen must all be removed when destroying it making abortcontroller + signal your standard pattern reduces mistakes 5 2 reduce listeners with delegation in a tv grid with hundreds to thousands of cards, attaching a click/keydown listener to each card keeps as many callback closures resident as there are listeners, plus register/unregister cost every time a card is created or discarded instead, attach a single listener to a parent container and use event bubbling with event target to determine which card fired it event delegation the listener count drops to one, cutting both memory and cleanup burden // bad a listener per card → n callback closures resident cards foreach card => card addeventlistener 'click', onclick ; // good delegate to a single container → 1 listener grid addeventlistener 'click', e => { const card = e target closest ' card' ; if !card return; handleselect card dataset id ; } ; it pairs especially well with virtualization 4 2 and element recycling 4 3 even when you swap card dom, the container's listener stays, so you don't have to re-register a listener each time cleanup is also just removing the one container listener, which reduces the listener-leak risk from 5 1 5 3 unremoved timers and callbacks if setinterval, settimeout, or requestanimationframe reference objects in their callbacks, those objects stay alive as long as the timer does in particular, setinterval and a recursive requestanimationframe run forever unless you explicitly stop them const timerid = setinterval update, 1000 ; // cleanup clearinterval timerid ; let rafid = requestanimationframe loop ; // cleanup cancelanimationframe rafid ; always stop animation loops and polling timers when the screen goes to the background or is destroyed you can detect visibility with the visibilitychange event 5 4 detached dom nodes a node removed from the dom but still referenced by a javascript variable is not gc'd this is called "detached dom" and is a classic type of web app leak // bad const cache = {}; const list = document getelementbyid 'list' ; cache oldlist = list; // keeps a reference list remove ; // removed from the dom // removed from the dom, but cache oldlist still holds it, so it isn't reclaimed including all child nodes // good cache oldlist = null; // cut the reference so it can be reclaimed 5 5 global caches/arrays that grow without bound logs, event records, response caches, and so on grow without limit if you keep piling them into global arrays/objects always cap the size e g , lru or clear periodically because map preserves insertion order, deleting and re-inserting an item on each access keeps the "least recently used" item at the front, giving you a simple lru // a simple bounded lru cache const max = 50; const cache = new map ; function get key { if !cache has key return undefined; const value = cache get key ; cache delete key ; // delete, then cache set key, value ; // re-insert → most-recently-used moves to the back return value; } function put key, value { if cache has key cache delete key ; // reset order on update else if cache size >= max { cache delete cache keys next value ; // evict the least-recently-used front } cache set key, value ; } 5 6 large objects held by closures a closure captures variables from its outer scope a single callback can keep a huge data structure alive reference only the values you actually need inside the callback 5 7 use weakmap / weakref when you want to attach extra info to an object without affecting its lifetime, use weakmap when the key object disappears, the related entry is reclaimed automatically const metadata = new weakmap ; metadata set domnode, { lastfocused 0 } ; // when domnode is gc'd, this entry disappears too 5 8 release memory aggressively when backgrounded as seen in section 1, when memory is low the tv kills background apps if your app holds a lot of memory while off-screen the user switched to another app/input , it becomes the first to be killed and reloads from scratch on return, hurting ux so the moment your app becomes invisible, give back memory you don't currently need release offscreen images / decoded bitmaps 3 2 return videos/decoders not currently playing 7 1 clear caches you can rebuild 5 5 stop animation loops / polling timers 5 3 detect show/hide with visibilitychange document hidden release resources the moment the app is hidden, and restore only what's needed when it becomes visible again document addeventlistener 'visibilitychange', => { if document hidden { releaseoffscreenimages ; // release image bitmaps pauseandreleasevideos ; // return videos not currently playing stopanimationloops ; // stop raf / timers trimcaches ; // clear caches you can rebuild } else { restoreonresume ; // on return, re-prepare only what's needed } } ; rule preserve lightweight state such as scroll position and focus, but drop heavy resources bitmaps, buffers, caches in the background and rebuild them on return 5 9 how to diagnose leaks in chrome devtools' memory panel, take two or more heap snapshots and compare them comparison to find objects that keep growing filter by "detached" to directly inspect detached dom nodes open and close the same screen several times, then compare snapshots; objects that keep accumulating even after closing indicate a leak 6 javascript memory and gc management 6 1 suppress memory churn memory churn means creating and discarding many temporary objects in a short time this makes gc run often, leading to animation frame drops and performance degradation watch these places especially inside animation loops requestanimationframe inside scroll handlers and input remote-control key handlers inside frequently running for loops // bad a new object/array every frame function onframe { const pos = { x computex , y computey }; // new object every frame → gc pressure moveto pos ; requestanimationframe onframe ; } // good reuse the object const pos = { x 0, y 0 }; function onframe { pos x = computex ; pos y = computey ; moveto pos ; requestanimationframe onframe ; } to maintain 60 fps, the budget per frame is about 16 ms to stay within it, minimize allocations during the frame 6 2 object pool — use with care returning reusable objects particles, card view-models, etc to a pool instead of discarding them can reduce allocation/gc but a pool has its own management cost, and an oversized pool actually increases resident memory and gc load apply it only where allocation is a real bottleneck hot paths , after measuring 6 3 choose efficient data structures choosing the right data structure for the job saves memory for large numeric data, use a typedarray int32array, float32array, uint8array, etc instead of a plain array [] it stores the values themselves densely in contiguous memory without creating a separate object per element, so the difference is large compared with, for example, an array of {x, y} objects for frequent key-value lookups, map is more suitable and clearer than an object {} ; for membership only, use set when handling large arrays, don't overuse unnecessary copies slice, spread [ arr], concat each copy allocates new memory // bad a million coordinates as an array of objects const points = []; for let i = 0; i < 1_000_000; i++ points push { x 0, y 0 } ; // good a single typedarray x, y interleaved const points = new float32array 1_000_000 * 2 ; 6 4 watch strings when concatenating string fragments many times, collecting them in an array and joining once with join is safer for memory and performance don't keep a huge response string in its original form for long extract only what you need, then cut the reference to the original so it can be reclaimed 6 5 cut references explicitly assign null to a large object you no longer need to cut the reference so gc can reclaim it especially when the variable's scope is wide or long-lived this bigdata = null; // release the large data note to empty a value, obj prop = null is generally better than delete obj prop delete can break the engine's internal object optimizations and hurt performance 6 6 your app code bundle takes memory too so far we've covered data created at runtime images, dom, objects , but your app's javascript/css code itself uses memory v8 parses loaded scripts and keeps them resident, so the bigger the bundle, the higher the baseline footprint from startup the less code you load, the less resident memory don't include unnecessarily large libraries instead of pulling in a heavy dependency whole for one or two features, use only the parts you need, or consider a lighter alternative or your own implementation also avoid including duplicate libraries that do the same thing minify + tree-shaking at build time to remove unused dead code code-split to load only per-screen code don't ship every route's code on the first screen; split with dynamic import import at entry time not loading unused screens' code into memory at all is best excessive abstraction layers increase code size the same principle as 4 5 removing unnecessary dom wrappers applies to code 7 video & media memory management the core of a tv app is mostly video playback media uses a lot of memory, so managing it matters 7 1 always release media resources that finished/left playback if you only remove a <video> element from the screen and leave it, the decoder and buffers may remain release it definitively in this order function releasevideo video { video pause ; video removeattribute 'src' ; // remove the src attribute // if you used <source> child elements, remove those too video load ; // prompt release of internal buffers/decoder // if you used mse, also clean up sourcebuffer/mediasource } don't keep multiple <video> elements alive at once when implementing preview autoplay etc , release a preview video the moment it leaves the screen a tv has a limited number of hardware video decoders, and each decoder uses large buffers reusing a single <video> element is safer than creating and discarding one per content item 7 2 mse media source extensions buffer management if you implement adaptive streaming yourself, remove the buffer for already-played ranges with sourcebuffer remove otherwise the buffer keeps growing and eats memory cap the forward buffer prefetching minutes ahead is convenient but uses a lot of memory be conservative with buffer size, especially at 4k / high bitrate 7 3 subtitle / thumbnail tracks don't keep thumbnail-preview scrub-preview sprites fully resident; load/release only the ranges you need reclaim used subtitle tracks and blob urls with url revokeobjecturl a blob url is not released unless you explicitly revoke it const url = url createobjecturl blob ; // after use url revokeobjecturl url ; 8 data handling optimization fetch / json / cache the amount and handling of data fetched over the network directly affect memory usage 8 1 fetch only as much as you need server-side filtering & paging don't fetch thousands of list items at once; split them via paging / infinite scroll don't receive large responses containing fields the client won't use design the api so the server filters down to only the needed fields e g , graphql field selection, a rest fields= parameter fetching, parsing, and storing large data itself leads to excessive memory use, which in turn degrades performance 8 2 understand the cost of json parse json parse str turns a string into an object tree, taking memory for the parsed objects on top of the original string the larger the json, the more this double occupancy hurts don't keep holding the original string reference after parsing cut it to allow reclamation for very large datasets, consider streaming parsing or partial parsing avoid the pattern of parsing everything and holding it all 8 3 don't hold parse results whole for long rather than keeping the entire list api response in memory, transform it into the shape the screen needs a view-model and then discard the original response in infinite scroll, cap and clean up data for pages already passed far from the screen data, like the dom, is a target for "windowing " 8 4 efficient data formats for large, repetitive data, use a compact representation rather than a verbose format json is usually the standard on the web, but consider the following avoid unnecessarily nested or verbose json structures; keep key names and structure compact don't stuff binary data into json as base64 about 33% bloat ; receive it as a separate binary response or an arraybuffer if very large structured data is sent repeatedly, consider binary serialization e g , protobuf, cbor 8 5 cap your caches cap response caches, image caches, and computed-result caches with an lru limit so they don't grow without bound see 5 5 don't put large data in localstorage/sessionstorage; they have size limits and are synchronous apis bad for performance if you truly need large data, use indexeddb — and still manage a cap 9 canvas / webgl / web audio / worker cautions more powerful web apis can use a lot of memory and often hold resources gc won't reclaim automatically the following applies only if you use these apis directly — skip it if you don't 9 1 canvas 2d canvas back-buffer memory = width × height × 4 bytes don't create many large canvases for an unused canvas, shrink its size to width = height = 0 to reclaim the back buffer, and cut the reference creating a large array every frame with getimagedata/putimagedata causes memory churn reuse buffers 9 2 webgl webgl resources textures, buffers, programs, framebuffers are not reclaimed by gc automatically delete them explicitly deletetexture, deletebuffer, deleteprogram, deleteframebuffer when you no longer need the context, release it with getextension 'webgl_lose_context' losecontext textures use large gpu memory by the same principle as image bitmaps control texture size and count 9 3 web audio close an audiocontext with close after use don't leave many open a decoded audiobuffer can be large; cut the reference when done 9 4 web worker a worker has its own memory space terminate a finished worker with terminate ; leaving it keeps occupying memory passing large data to a worker copies it structured clone , creating two copies use a transferable e g , transferring ownership of an arraybuffer to avoid the copy worker postmessage buffer, [buffer] ; // transfer ownership of buffer → no copy 9 5 iframe each iframe loads its own document and resources, using a lot of memory create ad / external-widget iframes only when needed, and remove them from the dom to destroy them completely when done in closing tv web app memory optimization boils down to three principles create only as much as you show — images at display size, lists only as far as visible, data only as much as needed release the moment it's no longer needed — clean up listeners, timers, observers, media, dom, and caches along their lifecycle measure and verify on a real device — confirm memory doesn't trend upward across repeated open/close and long runs apply these three principles from the design stage, and you'll deliver a smooth, stable app experience to users this document is a draft and will be continually improved through review refer to separate platform documentation for platform-specific policies and support details ↑ back to top
Develop Smart Hospitality Display
docweb app html5 memory optimization guide building memory-efficient web apps on constrained tv hardware audience third-party developers building web apps html5 for tv purpose principles and practices for using memory efficiently on constrained tv hardware runtime a chromium-based web engine on the tv blink renderer + v8 javascript engine nature of this document this is a best-practice guide it is not intended to mandate compliance or to trigger any action based on compliance — use it as a reference for better app quality and user experience contents why memory matters for tv web apps understanding the memory model of web apps image and graphics memory optimization dom management and list/grid virtualization preventing memory leaks javascript memory and gc management video & media memory management data handling optimization fetch / json / cache canvas / webgl / web audio / worker cautions 1 why memory matters for tv web apps tvs have different memory constraints than smartphones or pcs understanding this difference is the starting point of optimization memory is a shared resource when one app uses too much memory, it isn't only that app that slows down — the whole tv degrades app switching gets slower, other apps in the background get killed, and system animations stutter core principle memory is managed, not borrowed make "allocate when needed, release the moment it's no longer needed" the default for every feature you design 2 understanding the memory model of web apps to optimize, you first need to know where a web app's memory goes unlike native apps, a web app's memory is spread across several layers, much of which is not directly visible to the developer 2 1 major memory-consuming areas area description developer control v8 js heap javascript objects, arrays, closures, functions, etc high dom / render tree dom nodes, render objects, computed style high decoded images not the compressed file, but the bitmap expanded for display high gpu memory textures/layers compositing layers, textures, canvas back buffer medium media buffers video/audio decode buffers, mse sourcebuffer medium network & cache http cache, fetch responses, string buffers medium engine overhead v8/blink internal structures, fonts, parsers, etc low 2 2 "compressed file size" is not "memory usage" this is one of the most important concepts for understanding memory usage even a 100 kb jpeg must be decompressed into a bitmap to be displayed most pixels take 4 bytes rgba so a single 1920×1080 image = about 8 3 mb of decode memory 1920 × 1080 × 4 ≈ 8,294,400 bytes a single 4k 3840×2160 image is about 33 mb "it's a small file, so it's fine" is a misconception the real memory is the number of pixels actually drawn on screen × 4 bytes the same applies to json when you json parse a 200 kb json string, the original string stays in memory while the parsed object tree is created on top of it the parse result can take several times more memory than the original 2 3 the v8 heap and garbage collection gc javascript is a gc language gc only reclaims objects that are no longer reachable their references are cut conversely, if a reference remains anywhere, the object is never reclaimed this is the root cause of web app memory leaks gc isn't free while gc runs, the main thread can briefly pause, which shows up as animation frame drops v8 uses a generational gc newly created objects are reclaimed cheaply in the young generation, so creating and discarding temporary objects in ordinary code is not itself a problem the place to watch is hot paths that run tens to hundreds of times per second animation loops, scroll/input handlers, tight loops creating a new object every time there makes even a cheap gc run frequently, causing frame jank so the fix is not to reduce allocations blindly, but to reuse objects only in hot paths see 6 1 the cause of a leak is not "circular references" per se, but "something is still referencing it, so gc can't reclaim it " javascript's gc reclaims even a cyclic structure a→b, b→a correctly once nothing outside references it; the same is true for cycles involving dom nodes real leaks happen when global variables, long-lived arrays/objects, closures, or unremoved event listeners keep holding an object see section 5 3 image and graphics memory optimization images are the single largest memory consumer in almost every tv web app just following the principles in this section resolves most memory problems avoid animated gifs — they use a lot of memory use css animations for simple ui motion, and if you truly need raster animation, use animated webp instead of gif 3 1 load images at the size you display most important the key is not to download an image larger than the size you display and shrink it on the device don't shrink a large original into a small area putting a 1920×1080 original into a 200×300 thumbnail slot looks small on screen, but you still download that large original and decode it at least once — wasting network, cpu, and peak memory download images resized on the server to the display size thumbnails at thumbnail size, backgrounds at background size don't count on the browser to shrink it for you the browser sometimes downscales an image to the display size, but not always, and the cost of downloading and expanding the large original is incurred regardless the reliable way to save memory is to receive an image that is already at the display size <!-- bad using a 4k original for a thumbnail --> <img src="poster_4k jpg" style="width 200px; height 300px"> <!-- → small on screen, but the 4k original is still downloaded and decoded wasted network / cpu / memory --> <!-- good serve an image sized for display from the server --> <img src="poster_200x300 jpg" width="200" height="300"> 3 2 don't load offscreen images; release them when not visible lazy loading don't load images not yet visible from scrolling load them only as they approach the viewport, via the loading="lazy" attribute or intersectionobserver <img src="poster jpg" loading="lazy" width="200" height="300" alt=" "> actively release images that leave the screen in a long tv grid, reclaim decode memory for images far from the viewport by clearing src or removing the element see section 4 on virtualization // release image memory for a card far from the viewport function releaseimage imgel { imgel removeattribute 'src' ; // or imgel src = ''; imgel removeattribute 'srcset' ; } applying content-visibility auto to large offscreen sections can defer their rendering/layout cost and associated memory offscreen-section { content-visibility auto; contain-intrinsic-size 400px 300px; /* size hint */ } 3 3 treat css background images the same way an image set via background-image also uses bitmap memory once decoded background images of hidden elements can still load, so make large backgrounds load only when shown — by attaching a class at that moment 3 4 control decode timing suddenly attaching a large image to the screen can trigger a synchronous decode on the main thread and cause jank use the decoding="async" attribute or the img decode promise to decode ahead of time, asynchronously, before display const img = new image ; img src = 'poster jpg'; await img decode ; // after decode completes container appendchild img ; // attach without a frame drop 3 5 watch out for web fonts web fonts can be as large a single consumer as images, yet are often overlooked the browser keeps a font's glyphs resident in memory, and receiving a separate file per weight/style multiplies that in particular, languages with thousands to tens of thousands of glyphs, such as cjk chinese, japanese, korean , can have a single full-glyph font file reaching several mb, so take special care first ask whether you even need a web font if the tv platform's built-in fonts are enough, not using a web font at all is the surest saving load only the weights/styles you need limit to regular/bold and don't download unused light, thin, italic, etc use subset fonts building a font file that contains only the characters you actually use greatly reduces resident memory the more glyphs a language has, the bigger the effect unicode-range prevents downloading unused ranges when you split into multiple files, but to reduce resident memory itself, the fundamental approach is to prepare a subset file that contains only the glyphs you need use font-display swap so font loading doesn't block text rendering better perceived performance to reduce transfer size, use the well-compressed woff2 /* apply a subset only the needed glyphs to a specific character range */ @font-face { font-family 'appfont'; src url 'app-latin-subset woff2' format 'woff2' ; unicode-range u+0000-00ff; /* basic latin only */ font-display swap; } 4 dom management and list/grid virtualization after images, the biggest driver of a tv web app's memory is the number of dom nodes tv apps are especially vulnerable because they typically have long grid/carousel structures with hundreds to thousands of content cards 4 1 every dom node is memory each dom element carries a node object, computed style, layout info, and if displayed a render layer if you put thousands of cards into the dom at once, all of them stay resident even when only a few are visible as the dom grows, style recalculation, layout, and compositing costs grow too, hurting performance 4 2 virtualize long lists windowing — do it virtualization is a must for long lists/grids keep only the visible items plus a small buffer actually in the dom, and recycle as the user scrolls key ideas render only the items visible in the viewport + a margin above and below, not the whole list remove from the dom or recycle elements that scroll out of view fake the full scroll height with a spacer element or a transform // conceptual example simple windowing with intersectionobserver const buffer = 5; // extra items above and below function renderwindow startindex, endindex, alldata, container { container textcontent = ''; // remove previous items → reclaim dom nodes & image memory const frag = document createdocumentfragment ; for let i = startindex - buffer; i <= endindex + buffer; i++ { if i < 0 || i >= alldata length continue; frag appendchild createcard alldata[i] ; } container appendchild frag ; } in a tv app, any list with more than ~100 items should almost always consider virtualization during validation, apps that "render thousands of cards as one big dom" are a classic memory-overrun case 4 3 recycle elements — the dom version of the object pool pattern same idea as the object pool 6 2 creating and discarding a card every time increases dom create/destroy and gc load instead, create a fixed number of dom nodes and just swap their content // create only as many card dom nodes as can be visible, then reuse them const pool = []; function getcard { return pool pop || createcard ; } function recycle card { card queryselector 'img' removeattribute 'src' ; // release image memory pool push card ; } 4 4 clean up the previous screen's dom on navigation in an spa, leaving the previous screen's dom in place keeps occupying memory on routing, remove the previous view's dom and release its listeners, timers, and observers together giving each screen component a paired mount and destroy , where destroy undoes everything it created listeners, timers, observers, dom , structurally prevents leaks see section 5 4 5 reduce excessive wrapper / shadow dom unnecessary abstraction layers increase node count cut meaningless <div> nesting and excessive wrapper components, and remove unneeded layers to keep the dom shallow and simple 4 6 beware compositing-layer gpu memory blowup separate from dom node count, the browser promotes some elements to their own compositing layers, managed as gpu textures each layer takes width × height × 4 bytes of gpu memory, by the same principle as a decoded image a single full-screen layer is about 8 mb fhd , so if layers grow to dozens or hundreds, a tv with tight gpu memory hits its limit quickly layers arise mainly from forced-promotion hints like will-change, transform translatez 0 / translate3d 0,0,0 , or from animated elements don't slap translatez 0 on every card on the belief that "promoting to a layer makes it faster " it instead wastes gpu memory and increases compositing cost turn will-change on only right before an animation and remove it when done leaving it on permanently keeps that many layers resident don't stack multiple full-screen layers front overlays, fade layers, etc use the devtools layers panel or the "layer borders" option to check the actual layer count and sizes rule promote layers "only when needed, only on the elements that need it " don't sprinkle hints out of habit 5 preventing memory leaks a memory leak is when an object that is no longer needed can't be reclaimed because something still references it, and it is fatal in long-running tv apps the patterns that cause leaks in web apps are well known 5 1 unremoved event listeners the most common cause a listener registered with addeventlistener keeps holding the target object and the callback and everything its closure captures unless you explicitly remove it in particular, listeners on long-lived objects like window and document stay until the app ends // bad the listener remains after the component is removed, holding the whole object graph window addeventlistener 'resize', this onresize ; // good 1 always remove in a matching pair window removeeventlistener 'resize', this onresize ; // good 2 clean up all at once with abortcontroller const controller = new abortcontroller ; window addeventlistener 'resize', onresize, { signal controller signal } ; element addeventlistener 'keydown', onkey, { signal controller signal } ; // on cleanup controller abort ; // removes every listener registered with this controller at once rule listeners you register when creating a component/screen must all be removed when destroying it making abortcontroller + signal your standard pattern reduces mistakes 5 2 reduce listeners with delegation in a tv grid with hundreds to thousands of cards, attaching a click/keydown listener to each card keeps as many callback closures resident as there are listeners, plus register/unregister cost every time a card is created or discarded instead, attach a single listener to a parent container and use event bubbling with event target to determine which card fired it event delegation the listener count drops to one, cutting both memory and cleanup burden // bad a listener per card → n callback closures resident cards foreach card => card addeventlistener 'click', onclick ; // good delegate to a single container → 1 listener grid addeventlistener 'click', e => { const card = e target closest ' card' ; if !card return; handleselect card dataset id ; } ; it pairs especially well with virtualization 4 2 and element recycling 4 3 even when you swap card dom, the container's listener stays, so you don't have to re-register a listener each time cleanup is also just removing the one container listener, which reduces the listener-leak risk from 5 1 5 3 unremoved timers and callbacks if setinterval, settimeout, or requestanimationframe reference objects in their callbacks, those objects stay alive as long as the timer does in particular, setinterval and a recursive requestanimationframe run forever unless you explicitly stop them const timerid = setinterval update, 1000 ; // cleanup clearinterval timerid ; let rafid = requestanimationframe loop ; // cleanup cancelanimationframe rafid ; always stop animation loops and polling timers when the screen goes to the background or is destroyed you can detect visibility with the visibilitychange event 5 4 detached dom nodes a node removed from the dom but still referenced by a javascript variable is not gc'd this is called "detached dom" and is a classic type of web app leak // bad const cache = {}; const list = document getelementbyid 'list' ; cache oldlist = list; // keeps a reference list remove ; // removed from the dom // removed from the dom, but cache oldlist still holds it, so it isn't reclaimed including all child nodes // good cache oldlist = null; // cut the reference so it can be reclaimed 5 5 global caches/arrays that grow without bound logs, event records, response caches, and so on grow without limit if you keep piling them into global arrays/objects always cap the size e g , lru or clear periodically because map preserves insertion order, deleting and re-inserting an item on each access keeps the "least recently used" item at the front, giving you a simple lru // a simple bounded lru cache const max = 50; const cache = new map ; function get key { if !cache has key return undefined; const value = cache get key ; cache delete key ; // delete, then cache set key, value ; // re-insert → most-recently-used moves to the back return value; } function put key, value { if cache has key cache delete key ; // reset order on update else if cache size >= max { cache delete cache keys next value ; // evict the least-recently-used front } cache set key, value ; } 5 6 large objects held by closures a closure captures variables from its outer scope a single callback can keep a huge data structure alive reference only the values you actually need inside the callback 5 7 use weakmap / weakref when you want to attach extra info to an object without affecting its lifetime, use weakmap when the key object disappears, the related entry is reclaimed automatically const metadata = new weakmap ; metadata set domnode, { lastfocused 0 } ; // when domnode is gc'd, this entry disappears too 5 8 release memory aggressively when backgrounded as seen in section 1, when memory is low the tv kills background apps if your app holds a lot of memory while off-screen the user switched to another app/input , it becomes the first to be killed and reloads from scratch on return, hurting ux so the moment your app becomes invisible, give back memory you don't currently need release offscreen images / decoded bitmaps 3 2 return videos/decoders not currently playing 7 1 clear caches you can rebuild 5 5 stop animation loops / polling timers 5 3 detect show/hide with visibilitychange document hidden release resources the moment the app is hidden, and restore only what's needed when it becomes visible again document addeventlistener 'visibilitychange', => { if document hidden { releaseoffscreenimages ; // release image bitmaps pauseandreleasevideos ; // return videos not currently playing stopanimationloops ; // stop raf / timers trimcaches ; // clear caches you can rebuild } else { restoreonresume ; // on return, re-prepare only what's needed } } ; rule preserve lightweight state such as scroll position and focus, but drop heavy resources bitmaps, buffers, caches in the background and rebuild them on return 5 9 how to diagnose leaks in chrome devtools' memory panel, take two or more heap snapshots and compare them comparison to find objects that keep growing filter by "detached" to directly inspect detached dom nodes open and close the same screen several times, then compare snapshots; objects that keep accumulating even after closing indicate a leak 6 javascript memory and gc management 6 1 suppress memory churn memory churn means creating and discarding many temporary objects in a short time this makes gc run often, leading to animation frame drops and performance degradation watch these places especially inside animation loops requestanimationframe inside scroll handlers and input remote-control key handlers inside frequently running for loops // bad a new object/array every frame function onframe { const pos = { x computex , y computey }; // new object every frame → gc pressure moveto pos ; requestanimationframe onframe ; } // good reuse the object const pos = { x 0, y 0 }; function onframe { pos x = computex ; pos y = computey ; moveto pos ; requestanimationframe onframe ; } to maintain 60 fps, the budget per frame is about 16 ms to stay within it, minimize allocations during the frame 6 2 object pool — use with care returning reusable objects particles, card view-models, etc to a pool instead of discarding them can reduce allocation/gc but a pool has its own management cost, and an oversized pool actually increases resident memory and gc load apply it only where allocation is a real bottleneck hot paths , after measuring 6 3 choose efficient data structures choosing the right data structure for the job saves memory for large numeric data, use a typedarray int32array, float32array, uint8array, etc instead of a plain array [] it stores the values themselves densely in contiguous memory without creating a separate object per element, so the difference is large compared with, for example, an array of {x, y} objects for frequent key-value lookups, map is more suitable and clearer than an object {} ; for membership only, use set when handling large arrays, don't overuse unnecessary copies slice, spread [ arr], concat each copy allocates new memory // bad a million coordinates as an array of objects const points = []; for let i = 0; i < 1_000_000; i++ points push { x 0, y 0 } ; // good a single typedarray x, y interleaved const points = new float32array 1_000_000 * 2 ; 6 4 watch strings when concatenating string fragments many times, collecting them in an array and joining once with join is safer for memory and performance don't keep a huge response string in its original form for long extract only what you need, then cut the reference to the original so it can be reclaimed 6 5 cut references explicitly assign null to a large object you no longer need to cut the reference so gc can reclaim it especially when the variable's scope is wide or long-lived this bigdata = null; // release the large data note to empty a value, obj prop = null is generally better than delete obj prop delete can break the engine's internal object optimizations and hurt performance 6 6 your app code bundle takes memory too so far we've covered data created at runtime images, dom, objects , but your app's javascript/css code itself uses memory v8 parses loaded scripts and keeps them resident, so the bigger the bundle, the higher the baseline footprint from startup the less code you load, the less resident memory don't include unnecessarily large libraries instead of pulling in a heavy dependency whole for one or two features, use only the parts you need, or consider a lighter alternative or your own implementation also avoid including duplicate libraries that do the same thing minify + tree-shaking at build time to remove unused dead code code-split to load only per-screen code don't ship every route's code on the first screen; split with dynamic import import at entry time not loading unused screens' code into memory at all is best excessive abstraction layers increase code size the same principle as 4 5 removing unnecessary dom wrappers applies to code 7 video & media memory management the core of a tv app is mostly video playback media uses a lot of memory, so managing it matters 7 1 always release media resources that finished/left playback if you only remove a <video> element from the screen and leave it, the decoder and buffers may remain release it definitively in this order function releasevideo video { video pause ; video removeattribute 'src' ; // remove the src attribute // if you used <source> child elements, remove those too video load ; // prompt release of internal buffers/decoder // if you used mse, also clean up sourcebuffer/mediasource } don't keep multiple <video> elements alive at once when implementing preview autoplay etc , release a preview video the moment it leaves the screen a tv has a limited number of hardware video decoders, and each decoder uses large buffers reusing a single <video> element is safer than creating and discarding one per content item 7 2 mse media source extensions buffer management if you implement adaptive streaming yourself, remove the buffer for already-played ranges with sourcebuffer remove otherwise the buffer keeps growing and eats memory cap the forward buffer prefetching minutes ahead is convenient but uses a lot of memory be conservative with buffer size, especially at 4k / high bitrate 7 3 subtitle / thumbnail tracks don't keep thumbnail-preview scrub-preview sprites fully resident; load/release only the ranges you need reclaim used subtitle tracks and blob urls with url revokeobjecturl a blob url is not released unless you explicitly revoke it const url = url createobjecturl blob ; // after use url revokeobjecturl url ; 8 data handling optimization fetch / json / cache the amount and handling of data fetched over the network directly affect memory usage 8 1 fetch only as much as you need server-side filtering & paging don't fetch thousands of list items at once; split them via paging / infinite scroll don't receive large responses containing fields the client won't use design the api so the server filters down to only the needed fields e g , graphql field selection, a rest fields= parameter fetching, parsing, and storing large data itself leads to excessive memory use, which in turn degrades performance 8 2 understand the cost of json parse json parse str turns a string into an object tree, taking memory for the parsed objects on top of the original string the larger the json, the more this double occupancy hurts don't keep holding the original string reference after parsing cut it to allow reclamation for very large datasets, consider streaming parsing or partial parsing avoid the pattern of parsing everything and holding it all 8 3 don't hold parse results whole for long rather than keeping the entire list api response in memory, transform it into the shape the screen needs a view-model and then discard the original response in infinite scroll, cap and clean up data for pages already passed far from the screen data, like the dom, is a target for "windowing " 8 4 efficient data formats for large, repetitive data, use a compact representation rather than a verbose format json is usually the standard on the web, but consider the following avoid unnecessarily nested or verbose json structures; keep key names and structure compact don't stuff binary data into json as base64 about 33% bloat ; receive it as a separate binary response or an arraybuffer if very large structured data is sent repeatedly, consider binary serialization e g , protobuf, cbor 8 5 cap your caches cap response caches, image caches, and computed-result caches with an lru limit so they don't grow without bound see 5 5 don't put large data in localstorage/sessionstorage; they have size limits and are synchronous apis bad for performance if you truly need large data, use indexeddb — and still manage a cap 9 canvas / webgl / web audio / worker cautions more powerful web apis can use a lot of memory and often hold resources gc won't reclaim automatically the following applies only if you use these apis directly — skip it if you don't 9 1 canvas 2d canvas back-buffer memory = width × height × 4 bytes don't create many large canvases for an unused canvas, shrink its size to width = height = 0 to reclaim the back buffer, and cut the reference creating a large array every frame with getimagedata/putimagedata causes memory churn reuse buffers 9 2 webgl webgl resources textures, buffers, programs, framebuffers are not reclaimed by gc automatically delete them explicitly deletetexture, deletebuffer, deleteprogram, deleteframebuffer when you no longer need the context, release it with getextension 'webgl_lose_context' losecontext textures use large gpu memory by the same principle as image bitmaps control texture size and count 9 3 web audio close an audiocontext with close after use don't leave many open a decoded audiobuffer can be large; cut the reference when done 9 4 web worker a worker has its own memory space terminate a finished worker with terminate ; leaving it keeps occupying memory passing large data to a worker copies it structured clone , creating two copies use a transferable e g , transferring ownership of an arraybuffer to avoid the copy worker postmessage buffer, [buffer] ; // transfer ownership of buffer → no copy 9 5 iframe each iframe loads its own document and resources, using a lot of memory create ad / external-widget iframes only when needed, and remove them from the dom to destroy them completely when done in closing tv web app memory optimization boils down to three principles create only as much as you show — images at display size, lists only as far as visible, data only as much as needed release the moment it's no longer needed — clean up listeners, timers, observers, media, dom, and caches along their lifecycle measure and verify on a real device — confirm memory doesn't trend upward across repeated open/close and long runs apply these three principles from the design stage, and you'll deliver a smooth, stable app experience to users this document is a draft and will be continually improved through review refer to separate platform documentation for platform-specific policies and support details ↑ back to top
Develop Samsung Pay
docoverview with w3c payment request, you can offer samsung pay as a payment option on your website for customers making purchases on samsung mobile devices this integration is built on the w3c payment request api, which provides a standardized browser-based payment experience the following sections provide all the information you need to integrate w3c payment request prerequisites defines the requirements for using the w3c payment request service implement a w3c payment request integration gives step-by-step instructions for adding a w3c payment request-based payment checkout to your website, and managing the decrypted payment details received from samsung pay sample code offers an end-to-end sample code demonstrating a complete implementation from set-up to payment result handling user experience the w3c payment request integration supports 2 user experience models, depending on how you choose to present samsung pay on your website standard w3c model in the standard w3c model, your website presents multiple payment methods, including samsung pay when the customer taps your checkout button, the browser displays the standard payment sheet the customer can then select a payment method or add a new payment instrument, such as a debit or credit card selecting samsung pay opens the samsung pay payment sheet to complete the transaction branded samsung pay model if your website offers samsung pay as the only available payment method, you can use the branded samsung pay model where your website displays the buy with samsung pay button instead of a generic checkout button when the customer taps this button, the samsung pay payment sheet is displayed directly if additional information is required, such as a shipping address or contact details, the browser first presents the standard payment sheet to collect the information before proceeding to the samsung pay payment sheet if you need to offer multiple payment methods, use the standard w3c model
success story marketplace, mobile
blogwhat do successful apps and developers have in common? over the next five weeks we’ll be featuring profiles of successful devs who started with a kernel of an idea, a great team and some help from samsung, and went on to deliver apps are now being enjoyed by thousands. hear directly from these devs about their projects, their processes and the things that made the difference between success and failure in our five part series. to kick-off our ‘devs doing it right’ interview series, we spoke with catalin butnariu, the general manager of carbon incubator, a games incubator/accelerator with a mission to grow the games industry in eastern europe by helping local independent developers craft beautiful games and build viable businesses. you’re general manager at carbon incubator, whose mission is to grow the gaming industry in eastern europe by helping developers craft their games. tell us more about it. well, independent developers in eastern europe have always had a hard time finding funding, marketing, advice and support. there was a discussion between a small group of people from different companies in the area. we wanted to know how we could launch a business that would develop the local indie gaming industry in romania. and, about six months later, carbon incubator was born. our mandate is to help developers get their games off the ground by providing services such as mentoring, customer support, publishing support, quality assurance, trade show support, physical working space, equipment, and more. we even offer development grants. can you talk a little bit about some of the things you have learned about opening up new markets for games studios and growing an industry from the ground up? you have to be prepared to do a lot when you’re essentially starting a new industry. personally, i do a little bit of everything – from finding investors and building the overall game plan to setting up new partnerships, working with teams and getting involved in the daily projects. we’re very much an incubator, and as such, have to take on many different tasks. we have three full-time employees and a network of collaborators and mentors with industry experience in a wide range of specializations. we’re small, but we’re nimble and we like that. we don’t want to get too big just yet. what are some of the games you have created? last year, we picked up five games in different genres, and plan to find five more in 2017. our first game was link twin. i believe it was the first title ever to launch with the games for samsung program (replaced by galaxy store games). another exciting project is marble land, a physics-based vr puzzle game that offers a fun, immersive experience for people with gearvr headsets. we recently released a second game through the games for samsung program, called high on cake. we also have two other mobile titles and a pc game. what was the biggest technical hurdle you had to overcome when building link twin? one key hurdle we had to clear when building link twin was figuring out our tech stack. this needs to be done right at the beginning. your chosen tech stack can either create or alleviate a lot of problems. determining your tech stack should ultimately come down to what type of tools and features you want in each part of the game. it’s a decision that you should put a lot of time and thought into. it was a hurdle in our case because we had initially chosen some tools which seemed right at the time, but eventually created a bunch of issues and had to be changed. what do you see as the biggest opportunity for indie game developers in the future? there’s no magic formula for indie game developers to find success. it always helps to keep your eye on market trends, but the truth is you need to have an interest and expertise in whatever you end up doing. today, vr and ar are hot topics, but don’t limit yourself to these trends if you don’t have a real interest in them. my advice is to develop games for a platform you’re passionate about and that fits your particular skill set. as a developer, what new technologies/ trends are you most excited about? i’m a big fan of esports and see a lot of room for growth in the industry. but for developers, in general, i believe ar actually has more potential than vr. while it’s obviously great, vr can almost be too immersive because it requires an incredibly focused, dedicated experience. on the other hand, ar expands or adds things to a user’s current context, so it’s easier to use by more people. it offers enhanced interaction without the need for total immersion. do you have any words of wisdom for other indie game developers? share your ideas. i’m constantly running into developers who don’t want to showcase their games at events because they’re scared someone is going to steal their idea. but anyone can have a good idea. what it’s really about is execution— how is your game to play? is it actually fun? if you’re an indie game developer, you should be looking to get your ideas out in world as soon as possible. you need that feedback or validation, or otherwise you may waste months or years fine-tuning a game that might not even be that fun to play. what are your thoughts on the games for samsung program? it has been an awesome experience. when link twin was accepted into the program in july 2016, we were all very excited. being selected by samsung was validation for our business— we could use it as our company’s calling card. the samsung team was also extremely supportive every step of the way. the biggest benefit was in creating visibility for the game after its release. this is gold for any developer. when you’re just starting out, one of the biggest challenges is getting reach for a game, but thanks to samsung’s promotion we were able to attract those initial users, and link twin ended up reaching 200,000 downloads on the galaxy app store. this was an excellent result for us. finally, a question we’re asking all of our ‘devs doing it right’: what features do all great/successful apps have in common? they have to ultimately appeal to your target audience. this may sound overly simplistic, but the successful games are the ones that are fun to play. i’ve seen a ton of projects from a lot of different indie studios, and while some may have good monetization schemes and/or excellent graphics, their games just aren’t fun to play. at the end of the day, you need to develop something that people enjoy.
Catalin Butnariu
Develop Samsung Wallet
docrest api authorization token jwt / jws this section defines the authorization token used to authorize rest api calls and bind each token to a specific request http transmission this subsection defines how the authorization token is carried in api calls the authorization token shall be transmitted via the http header header name authorization scheme bearer format authorization bearer <jwt> compatibility note some examples may omit the bearer scheme for brevity; producers should send bearer, and verifiers should tolerate both formats for backward compatibility payload binding rules this subsection defines the request-binding behavior required for authorization tokens the authorization token payload binds the token to the exact request using api method api path token generators must populate these fields using the request that will actually be transmitted, and token validators must verify that the bound values match the received request method/path data structures authorization token jws header authorization token this subsection defines the required header fields for the authorization token field description algstring 16 required signing algorithme g , rs256 ctystring 16 required content typeset as "auth" verstring 4 required token versionset as "3" certificateidstring 64 required certificate identifier issued when csr/certificate is registered during onboarding partneridstring 16 required partner identifier assigned at partner portal registration same as partnercode utclong 13 required creation time epoch ms used for expiry / anti-replay time checks i e , utc+0 jws payload authorization token this subsection defines required and optional payload fields for the authorization token field description apiobject required current api binding object api methodstring 8 required http method of the request e g , get/post api pathstring 512 required http path of the request path only, excluding scheme/host/query e g , /wltex/cards/{cardid}/notification refidstring 256 optional unique content identifier defined by the partner authenticationstring 2048 optional authentication value; see section 3 4 * should be provided as an escaped json string updatedatlong 13 optional content update timestamp epoch milliseconds e g , 1715078400123
We use cookies to improve your experience on our website and to show you relevant advertising. Manage you settings for our cookies below.
These cookies are essential as they enable you to move around the website. This category cannot be disabled.
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.
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.
You have successfully updated your cookie preferences.