Articles in this section
Category / Section

How to implement neostore sdk "Add to wallet" button on your website?

Published:
Updated:

The neostore cinto SDK allows developers to add a “add to wallet” button on a website.

UI Result

On iOS mobile device

Sans_titre_500_x_250_px.png

On Android mobile device

Sans_titre_500_x_250_px_1.png

Otherwise

Sans_titre_500_x_250_px_2.png

This button redirects the user to neostore pass layout which looks like this :

release.png

How to display the button ?

Through vanilla javascript

<script type="text/javascript">
(function (n, e, o) {
var s=n.createElement("script");s.src="https://sdk.neostore.cloud/scripts/"+e+"/cinto@1";s.async=1;
s.onload=function(){neostore.cinto.initialize(e,o)};n.body.appendChild(s);
})(document, "molia", {});
</script>

<div data-neostore-addToWalletButton data-neostore-passId="KlnqcxVLA9pS4ol5"></div>

The third parameter may contains any option available for neostore, to force a language use

<script type="text/javascript">
(function (n, e, o) {
var s=n.createElement("script");s.src="https://sdk.neostore.cloud/scripts/"+e+"/cinto@1";s.async=1;
s.onload=function(){neostore.cinto.initialize(e,o)};n.body.appendChild(s);
})(document, "molia", { language: "fr" });
</script>

<div data-neostore-addToWalletButton data-neostore-passId="KlnqcxVLA9pS4ol5"></div>

Through npm module

npm install @neostore/cinto
// install cinto SDK through npm install @neostore/cinto
import { AddToWalletButton } from "@neostore/cinto";

const btn = new AddToWalletButton("molia", {
    language: "fr",
    passId: "KlnqcxVLA9pS4ol5",
});
btn.render(document.getElementById("btn"));

Options

export interface Options {
    /**
     * Neostore URI environment to use.
     * @default: <https://app.neostore.cloud>
     */
    environment: string;
    /**
     * Identifier of the tenant.
     */
    tenantId: string;
    /**
     * Name of the pass layout to redirect the user when the user is on desktop
     * @default: pass
     */
    passLayoutName: string;
    /**
     * ISO 639 language code to use to do display the button. When omitted, the language will be automatically detected based on the browser settings.
     * If the value doesn't match any available option, the browser settings will be used otherwise english will be used.
     * @default: undefined
     */
    language?: string;
    /**
     * Identifier of the pass to display or promise of it
     */
    passId: string;
    /**
     * Platform to use to display the button. When omitted, the platform will be automatically detected based on the user agent.
     * Possible values are: "apple", "google" or "desktop"
     *
     * @default: undefined
     **/
    platform?: Platform;
    /**
     * External identifiers to use to aquire the passId
     */
    externalIdentifiers?: Record<string, { value: string; hmac?: string }>;
    /**
     * Pass type to use to aquire the passId
     * Required when externalIdentifiers is set
     */
    passType?: string;

    /**
     * Callback called when the button is clicked
     */
    onClick?: (e: MouseEvent, data: { options: Partial<Options>; platform: Platform }) => void;

    /**
     * Callback when an error occurs
     *
     */
    onError?: (error: string) => void;
}

Concepts

Platform detection

When using the data-neostore-addToWalletButton data attribute, the component will automatically render the right button with the defined callback:

  • desktop: redirects to the pass page from Neostore
  • apple: displays the add to wallet call to action that downloads the pass
  • android: displays the add to google wallet that opens the google page

This can be overriden by using data-neostore-platform="desktop" (“desktop”, “apple”, or “google” options)

Language detection

The component is looking for the user browser’s language and fallback to en either if the locale doesn’t exist for the given platform or if the user language is not supported.

You can force the language by using data-neostore-language="fr"

Customization

The general layout is:

  • a div of your responsibility
    • a link, selector: .neostore-link
      • an image, selector .neostore-img and .neostore-link-{{ platform }} where platform is desktop, apple, android

The android and google buttons respects the google and apple design guidelines for this action. Apple guideline Google Guideline

You can still update some styles using css but remember that the link contains an image.

Advanced usage

desktop render customization

On desktop, the principal information is the url where you should redirect your users.
You can update the way you render this link to display a QR Code for example using the following snippet

<script src="https://cdn.rawgit.com/davidshimjs/qrcodejs/gh-pages/qrcode.min.js"></script>

<div id="qrcode"></div>
<script type="module">
    import { AddToWalletButton } from "https://sdk.neostore.cloud/scripts/molia/cinto@1/cinto.mjs;

    const button = new AddToWalletButton("molia", {
        passType: "user",
        passId: 'KlnqcxVLA9pS4ol5'
    });
    const url = await button.getPassPageUrl();
    new QRCode(document.getElementById("qrcode"), url);
</script>

using external identifiers with hmac

You can retrieve the passId parameters through neostore API. It is also possible to use the externalIdentifiers and the hmac_sha256 value of the identifiers.

hmac_sha256 should be computed with one of the neostore secret.

For example, for a customerId of SC103010 and a secret of I1M8emrrJSns4Hnuibbm45eWfLQMosPGKSp1JzKsCrXeWmhjE8lZhxC2tfSRX5IJ the hmac value should be 8c5a9ebdd9b4ac8d2307cc34192f0faed441ef724c043162f0618784173d4d93

CyberChef

Exemple of hmac computing

using data-attribute

<script type="text/javascript">
(function (n, e, o) {
var s=n.createElement("script");s.src="https://sdk.neostore.cloud/scripts/"+e+"/cinto@1";s.async=1;
s.onload=function(){neostore.cinto.initialize(e,o)};n.body.appendChild(s);
})(document, "molia", { language: "fr" });
</script>

<div data-neostore-addToWalletButton
     data-neostore-passType="user"
     data-neostore-externalIdentifiers-y2.customerId-value="SC103010"
     data-neostore-externalIdentifiers-y2.customerId-hmac="cbbcfc5xxxxx"
></div>

using component

// install cinto SDK through npm install @neostore/cinto
import { AddToWalletButton } from "@neostore/cinto";

const btn = new AddToWalletButton("molia", {
    language: "fr",
    externalIdentifiers: {
        "y2.customerId": {
            value: "SC103010",
            // hmac_sha256 of customerId with one of the neostore secret
            hmac: "cbbcfc5xxxxx",
        },
    },
});
btn.render(document.getElementById("btn"));

How to get passId information ?

When the pass already exists

Untitled (57).png

  1. Get an API Key on the neostore back-office :
    1. https://admin.neostore.cloud/tenant/~/apiKeys
    2. API Key should have the tenant.pass:read permission

Untitled (59).png

  1. Call the /passes API

    https://app.neostore.cloud/api/{tenantId}/passes?filter=filter[0].field=y2.customerId&filter[0].operator=equals&filter[0].value=04101234

    **curl -X 'GET' \ 'https://app.neostore.cloud/api/{tenantId}/passes?filter%5B0%5D.field%3Dy2.customerId%26filter%5B0%5D.operator%3Dequals%26filter%5B0%5D.value%**04101234**\ -H 'accept: text/plain' \ -H 'X-API-KEY: {apiKey}'**

    Result will be something like:

    [
    	{
    		"id": "KlnqcxVLAxxxxxx",
    		"secret": "r9vuaWOxxxxxxx",
    		"creationDate": "2023-07-19T11:56:34.9418747Z",
    		"passType": "user",
    		"identifiers": {
    			"y2.customerId": "04101234"
    		},
    		"providers": {
    			"apple": {
    				"installationDate": "2023-07-19T11:56:43.2830295Z",
    				"devices": [
    					{
    						"deviceIdentifier": "5dec2xxxxxx",
    						"pushToken": "5f7f23xxxxx",
    						"registrationDate": "2023-07-19T11:56:43.2830295Z"
    					}
    				]
    			}
    		}
    	}
    ]
    

When the pass doesn’t exists

Third party integration

Shopify

use this liquid placeholder to display the add to wallet button :

{% assign tenantId = 'molia' %}
{% assign secret = 'sTyQrYOKtkIfT77K4wns8cUxfOTV7n57RFztd59wPNqvyvZ9sNnCy3a3s8cikz9z' %}

{% assign y2Identifier = 'SC103010' %}
{% assign y2Identifierhmac = y2Identifier | hmac_sha256: secret %}

<script type="text/javascript">
(function(n,e,o){var s=n.createElement("script");s.src="https://sdk.neostore.cloud/scripts/"+e+"/cinto";s.async=1;s.onload=function(){neostore.cinto.initialize(e, o);};n.body.appendChild(s);})
(document, {{ tenantId }});
</script>

<div
  data-neostore-addToWalletButton
  data-neostore-passType='user'
  data-neostore-externalIdentifiers='{"y2.customerId":{"value":"{{ y2Identifier }}","hmac":"{{ y2Identifierhmac }}"} }'>
</div>

React integration

It is possible to encapsulate the button within React using this component. You can adjust your component based on your needs :

import { AddToWalletButton } from "@neostore/cinto";

const CintoMobileAddToWallet = React.forwardRef<
    HTMLButtonElement,
    BoxProps & {
        passId?: string;
    }
>(({ passId, ...props }, buttonRef) => {
    const localRef = useRef<HTMLButtonElement>(null);
    buttonRef = buttonRef || localRef;

    const ctaRef = useRef<HTMLDivElement>(null);
    const cintoButtonRef = useRef<AddToWalletButton>();

    useEffect(() => {
        cintoButtonRef.current = passId
            ? new AddToWalletButton(tenantId, {
                  passId,
              })
            : undefined;
        ctaRef.current && cintoButtonRef.current?.render(ctaRef.current);
        if (passId && cintoButtonRef.current) {
            cintoButtonRef.current?.perform();
        }
    }, [passId, tenantId]);

    return (
        <Box {...props}>
            <div ref={ctaRef} />
        </Box>
    );
});

export default CintoMobileAddToWallet;
Access denied
Access denied