Embedded login

Make the payment journey faster for your end users

πŸ“˜

Prerequisites

You have completed the registration in the Merchant Portal and have made your first one-time payment. If you have not done these two steps, go back and create your Merchant Portal account by clicking here.

Embedded login flow setup

In the regular checkout flow if the user does not sign in then they have to perform two SCAs (Strong Customer Authentication). If they would like to pay with a single SCA in the future then they have to explicitly sign up and sign in at the start of their payment flows. With the embedded login the users of Checkout are able to pay with a single SCA without the need to explicitly sign up or sign in.
In this flow the merchant provides the user's email in the checkout request and upon finishing the payment flow a profile is created. Through the webhook the merchant is provided a unique id which can be sent in future checkout requests for the same user so that they can pay with a single SCA.
To prepare for the embedded login flow an RSA key pair has to be generated through the merchant portal.

Generating the RSA key pair through the merchant portal

In this step an RSA key pair will be generated for the merchant. The private key will be used to sign the token that contains the information necessary for single SCA flows. This key is generated in the browser and never leaves the user's computer during this flow. The public key will be used by Checkout to validate the signature.

  1. Log in to Merchant Portal.
  2. Navigate to Company Settings in the sidebar.
  3. Under the Integration tab look for the Embedded login key pair section. If you have previously generated a key pair then here you can see the date the key was generated.
  4. On the right of the section you can initiate a (re)generation by pressing the button with the refresh icon.
  5. The user will be prompted to authenticate again.
  6. The private key is downloaded to the user's computer in a .pem file and the public key is saved.
    If the private key is lost there is no way to recover it thus a new key pair has to be generated. The key pair generation invalidates all previous keys. Previously received unique ids can be signed with the new private key so the user experience won't degrade as long as the new private key is updated as soon as possible. Treat the private key as a secret as it can be used to access sensitive user information.

The contents of the saved .pem file will look like this:

-----BEGIN PRIVATE KEY-----  
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCw8ULZ+Xc7lp4b  
p4iE43Td4xf/MltvL0nYUtraCC0BpAXK2NjKKIu69BeC/4quEj3OBsJlK+SKpg/v  
(...)  
zkdsd3otKfZU3Nn9W35ycw7p6DXvVHV9NniecAzZJehSrsuQlqF7o8dSVozyeW58  
mjN8cOvIhUe+4v3Rhp6ykK6b  
-----END PRIVATE KEY-----

Modifying the checkout request to activate the embedded login flow

A checkout request without the embedded login flow is structured like this:

{
    "referenceId": "41212",
    "amount": 100,
    "currency": "NOK",
    "clientName": "Awesome Scooters AS",
    "remittanceInfo": "Something we need to provide",
    "successUrl": "https://merchant.com/order/41212/success",
    "failUrl": "https://merchant.com/order/41212/fail",
    "cancelUrl": "https://merchant.com/order/41212/cancel",
    "language": "EN"
}

To turn on the embedded login flow add the userEmail field to the request and populate it with the user's email. If this user doesn't exist in our system yet then at the end of the payment flow a profile will be created for them. Otherwise they will be able to log in and consent so that the merchant can use the embedded login flow for their profile.

{  
    ...,  
    "userEmail": "[email protected]"  
}

If a profile is created or the user agrees to embedded login, then the message sent through the webhook will contain two additional fields:

{  
    ...,  
    "embeddedUserId": "9ebbc64b-e5e6-44d1-9e60-e5f8af3947ba",  
    "embeddedLoginConsentExpiration": "2023-11-09T12:46:38.413007"  
}

The embeddedUserId contains the unique id that's going to be needed for the single SCA flow while the embeddedLoginConsentExpiration contains the date when the user's consent will automatically expire. The latter is refreshed on every completed payment.
To turn on the single SCA flow the Checkout needs a userToken field. The token generation will be explained below. So, with a token the checkout request should look like this:

{  
    ...,  
    "userEmail": "[email protected]",  
    "userToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWJlZGRlZFVzZXJJZCI6IjllYmJjNjRiLWU1ZTYtNDRkMS05ZTYwLWU1ZjhhZjM5NDdiYSIsImlhdCI6MTY5MDM1ODkzMH0.f4q(...)6Kz4mdlccav735EA"  
}

If the token is valid and the user's consent is not expired, then they will be able to pay with a single SCA. In case there are problems with the token or the user's consent then the API doesn't error, but the user will see the regular checkout flow.

Generating the token

Payload
The userToken is a signed JWT which contains the embeddedUserId previously received through the webhook and an iat field:

{  
  "embeddedUserId": "9ebbc64b-e5e6-44d1-9e60-e5f8af3947ba",  
  "iat": 1690355492  
}

Claims:
β€’ embeddedUserId: Should contain the embeddedUserId received through the webhook. If the id does not belong to the userEmail sent in the checkout request then the token is considered invalid.
β€’ iat: The usual JWT iat field (issued at epoch timestamp). The JWT is expected to have been issued close to the time of the checkout request.

Signature
To sign the token the RS256 (RSASSA-PKCS1-v1_5 using SHA-256) algorithm must be used with the private key generated through merchant portal.

An example in Java using the java-jwt library:

public class EmbeddedLoginTokenService {
    public String generateToken(String embeddedUserId, String privateKey) {
        // Load the private key
        RSAPrivateKey rsaPrivateKey = loadPrivateKey(privateKey);

        // Create and sign the token with the required claims
        String token = JWT.create()
                .withClaim("embeddedUserId", embeddedUserId)
                .withIssuedAt(Date.from(Instant.now()))
                .sign(Algorithm.RSA256(null, rsaPrivateKey));

        return token;
    }

    private RSAPrivateKey loadPrivateKey(String privateKey) {
        // Remove PEM headers
        privateKey = privateKey.replace("-----BEGIN PRIVATE KEY-----", "")
                .replace("-----END PRIVATE KEY-----", "")
                .replaceAll("\\s", "");

        // Decode the private key from Base64
        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKey);

        // Create a PKCS8EncodedKeySpec from the private key bytes
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);

        try {
            // Create a KeyFactory for RSA
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");

            // Generate the PrivateKey
            return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return null;
        }
    }
}