Manual Provisioning

The complete callback sequence when a cardholder types their card into a wallet, and exactly what you must return at each step.

Manual provisioning is when the cardholder opens Apple Pay or Google Pay and types the card number, expiry and CVV in by hand. Because anyone who can read a card number could do that, the cardholder must prove they own the card: either by entering a one-time code you deliver, or by logging in to your app, which the wallet opens for that purpose.

This is the path with the most work on your side. UnDosTres cannot complete it alone, because UnDosTres does not hold your cardholder's phone number or email address, and UnDosTres cannot log the cardholder in to your app. You do both.

This page covers the code route first, message by message, then verification in your app, which shares the start and the end of the sequence and replaces the middle.

The full sequence, code route

%%{init: {"sequence": {"actorMargin": 8, "boxMargin": 6, "noteMargin": 6, "messageMargin": 26, "width": 110, "diagramMarginX": 8, "diagramMarginY": 8}}}%%
sequenceDiagram
    autonumber
    participant CH as Cardholder
    participant W as Wallet
    participant MC as Mastercard MDES
    participant UDT as UnDosTres
    participant P as You

    CH->>W: Types card number, expiry, CVV
    W->>MC: Checks the card is eligible
    MC-->>W: Terms and conditions
    W-->>CH: Displays the terms
    CH->>W: Accepts
    W->>MC: Starts digitization

    Note over MC,P: Step 1 - contact methods
    MC->>UDT: Requests activation methods
    UDT->>P: digitization.activationmethods
    P-->>UDT: Masked phone and email
    UDT-->>MC: Activation methods
    MC-->>W: Options
    W-->>CH: How do you want to verify
    CH->>W: Chooses SMS

    Note over MC,P: Step 2 - deliver the code
    MC->>MC: Generates the activation code
    MC->>UDT: Activation code
    UDT->>P: digitization.activation
    P->>CH: Sends the code by SMS
    CH->>W: Enters the code
    W->>MC: Submits the code
    MC->>MC: Validates the code

    Note over MC,P: Step 3 - done
    MC->>MC: Activates the token
    MC->>UDT: Digitization complete
    UDT->>P: digitization.complete
    P->>CH: Confirms card added
    MC-->>W: Provisioning complete
    W-->>CH: Card appears in the wallet

How the callbacks reach you

All four messages arrive as XML-RPC AdministrativeMessage calls to your endpoint, in the same shape as every other administrative message on this platform. See Administrative Message for the envelope.

Seven parameters, in order:

#ParameterNotes
1terminalIDYour terminal ID, 10 characters
2referenceThe wallet reference the message concerns
3messageNameWhich message this is. The four values below
4messageDataThe payload, KLV encoded
5transactionIDGenerated by the caller. Not unique, may repeat
6transactionDateISO 8601
7checksumHMAC-SHA256 of the method name concatenated with all argument values, keyed with your terminal password

You respond to all of them with resultCode. 1 means accepted.

The four messages, and their full field-level reference:

messageNameReferenceCovered below
digitization.activationmethodsReferenceStep 1
digitization.activationReferenceStep 2
digitization.completeReferenceStep 3
digitization.event.Digitization_ExceptionReferenceStep 4

1. digitization.activationmethods

When: at the very start, once the cardholder has accepted the terms.

What it means: a provisioning attempt has started and MDES needs to know how the cardholder can be reached, so the wallet can offer them a choice.

What you must return: the contact methods available for this cardholder. This is the one message where your response carries real data rather than just an acknowledgement.

KLV keys present: 910 digitized device id, 915 digitized token requestor id, 929 digitization path, 930 wallet recommendation, 931 tokenization pan source.

Activation methods

TypeMethod
1Masked mobile phone number
2Masked email address
3Call to an automated call centre, initiated by the cardholder
4Call to a staffed call centre, initiated by the cardholder
5Website
6Mobile application
7Issuer voice call to the cardholder's phone

Return only the methods you can actually service. If you return type 1, you must be able to send an SMS. If you return type 4, you must have a call centre that can authenticate a cardholder and act on the result.

Type 6 is your own app. It is the route described in Verifying in your app, and on this programme it is configured as the first method offered for both Google Pay and Apple Pay, with the SMS code second. Whether your response has to list it, or whether the scheme adds it from the product configuration UnDosTres maintains, is being confirmed; until then keep returning types 1 and 2 as shown below.

📘

Mask the values you return. There is no scheme-mandated masking format, but the convention is:

  • Email: keep the first two characters and the whole domain, star the rest. ag*****@gmail.com
  • Phone: star everything except the last four digits, keeping any hyphens. **-***-****-1234

The masked value is displayed to the cardholder inside the wallet, so it has to be recognisable to them and useless to anyone else.

Response

<methodResponse>
  <params>
    <param>
      <value>
        <struct>
          <member>
            <name>resultCode</name>
            <value><int>1</int></value>
          </member>
          <member>
            <name>activationMethods</name>
            <value>
              <array>
                <data>
                  <value>
                    <struct>
                      <member><name>type</name><value>1</value></member>
                      <member><name>value</name><value>555-444-2222</value></member>
                    </struct>
                  </value>
                  <value>
                    <struct>
                      <member><name>type</name><value>2</value></member>
                      <member><name>value</name><value>[email protected]</value></member>
                    </struct>
                  </value>
                </data>
              </array>
            </value>
          </member>
        </struct>
      </value>
    </param>
  </params>
</methodResponse>
❗️

If you return nothing here, provisioning stops. An empty or failed response means the wallet has no way to verify the cardholder, and the attempt dies. This is the single most common cause of a manual provisioning that silently never completes.


2. digitization.activation

When: after the cardholder picks a contact method: SMS, email or a voice call. When the cardholder picks your app instead, this message is not expected, because there is no code for you to deliver; see Verifying in your app.

What it means: MDES has generated the activation code. You deliver it to the cardholder over the channel they chose.

What you must return: resultCode only.

KLV keys present: 901 activation code, 902 activation method type, 903 activation method value, 904 activation expiry, 915 digitized token requestor id.

The code is generated and validated by MDES, not by you. You are the delivery channel. Do not generate your own code, do not modify it, and do not validate it. Read 901, send it, and acknowledge.

Note 904, the expiry. Deliver promptly; a code that arrives after it expires produces a digitization.event.Digitization_Exception and a confused cardholder.


3. digitization.complete

When: MDES has validated the code and activated the token.

What it means: the card is now live in the wallet.

What you must return: resultCode only.

KLV keys present: 254 digitized pan, 255 digitized wallet id, 910 device id, 911 pan expiry, 912 masked FPAN, 913 Token Unique Reference, 915 token requestor id, 923 event type, 924 event reason code.

❗️

This is your trigger to notify the cardholder. Wallet programmes require the issuer to tell the cardholder, over a channel they already trust, that their card was added. Send an in-app message, SMS or email on receipt of this callback. It is a launch requirement, not a nicety, and it is also the main way a cardholder finds out if someone else added their card.

Store 913, the Token Unique Reference. It is the identifier you pass as token_reference for every lifecycle operation afterwards.


4. digitization.event.Digitization_Exception

When: the provisioning attempt failed.

What it means: one of the following happened:

  • the activation code retries were exceeded
  • an expired activation code was used
  • an invalid activation code was used
  • an incorrect activation code was entered

What you must return: resultCode only.

KLV keys present: the same set as digitization.complete.

Treat this as a closed, failed attempt. The cardholder may start again from scratch. If you are tracking provisioning funnels, this and digitization.complete are your two terminal outcomes.


Verifying in your app instead of with a code

The code is not the only way a cardholder can prove ownership. Both wallets can hand the cardholder over to your app, let you authenticate them the way you always do, with their login, PIN or biometrics, and take the result back. Google calls this app-to-app verification; Apple calls it In-App Verification. It is activation method type 6 in the table above, and it is the method cardholders prefer when it is offered, because nothing has to arrive by SMS.

On this programme the mobile application is configured as the first verification method for Google Pay and for Apple Pay, with the SMS code as the second. It is not an optional extra: a cardholder who has your app installed will be offered it first, and your app has to be able to finish the job.

Two rules shape the whole route:

  • It is only offered on manual provisioning. When the cardholder starts from your own app, with push provisioning, you have already authenticated them, and the wallets do not allow your app to be offered as the verification step a second time.
  • Your app must handle both the hand-off and the later case. A cardholder may pick your app in the wallet and be sent to it immediately, or may leave the card pending in the wallet and open your app on their own a day later. Both must end in an activated token, so your app checks for pending tokens at every login, not only when the wallet opens it.

The sequence, app route

%%{init: {"sequence": {"actorMargin": 8, "boxMargin": 6, "noteMargin": 6, "messageMargin": 26, "width": 110, "diagramMarginX": 8, "diagramMarginY": 8}}}%%
sequenceDiagram
    autonumber
    participant CH as Cardholder
    participant W as Wallet
    participant MC as Mastercard MDES
    participant UDT as UnDosTres
    participant APP as Your app
    participant BE as Your backend

    CH->>W: Types the card, accepts the terms
    W->>MC: Starts digitization
    MC->>UDT: Requests activation methods
    UDT->>BE: digitization.activationmethods
    BE-->>UDT: Masked phone and email
    UDT-->>MC: Activation methods
    MC-->>W: Options, including your app
    W-->>CH: How do you want to verify
    CH->>W: Chooses your app

    Note over W,APP: Step A - hand-off
    W->>APP: Opens your app with the token reference
    APP->>APP: Checks the caller is the wallet
    CH->>APP: Logs in
    APP->>APP: Finds the pending token, checks ownership

    Note over MC,BE: Step B - activation code
    APP->>BE: Requests activation
    BE->>UDT: GenerateTimeBasedSecret
    UDT-->>BE: secret, valid 60s
    BE-->>APP: card_id + secret
    APP->>APP: Gets the activation code via the SDK
    APP->>W: Returns the cardholder with the code
    W->>MC: Submits the code
    MC->>MC: Validates the code, activates the token

    Note over MC,BE: Step C - done
    MC->>UDT: Digitization complete
    UDT->>BE: digitization.complete
    BE->>CH: Confirms card added
    MC-->>W: Provisioning complete
    W-->>CH: Card appears in the wallet

Steps 1 to 9 are the same as the code route. Steps 10 to 21 replace digitization.activation and the SMS. Steps 22 to 26 are the same ending.

Steps A and B in detail, with the SDK

The two self-messages on "Your app" above are where the SDK does its work. This is the same stretch with the SDK as its own participant and the Android method names on the arrows. The iOS and React Native names are in the table that follows.

%%{init: {"sequence": {"actorMargin": 8, "boxMargin": 6, "noteMargin": 6, "messageMargin": 26, "width": 110, "diagramMarginX": 8, "diagramMarginY": 8}}}%%
sequenceDiagram
    autonumber
    participant W as Wallet
    participant APP as Your app
    participant SDK as SDK
    participant BE as Your backend
    participant UDT as UnDosTres

    Note over W,SDK: Step A - hand-off
    W->>APP: Intent: package, action, EXTRA_TEXT
    APP->>APP: getCallingPackage() is com.google.android.gms
    Note over APP: Cardholder logs in
    APP->>SDK: getTokenInfo(intent)
    SDK-->>APP: token reference, last four, state
    APP->>APP: State NEEDS_IDENTITY_VERIFICATION, card is this cardholder's

    Note over APP,UDT: Step B - activation code
    APP->>BE: Activation request (card_id)
    BE->>UDT: GenerateTimeBasedSecret
    UDT-->>BE: secret, valid 60s
    BE-->>APP: card_id + secret
    APP->>SDK: getActivationCode(card_id, secret)
    SDK->>UDT: Activation code request
    UDT->>UDT: Verifies the secret
    UDT-->>SDK: Activation code
    SDK-->>APP: Activation code
    APP->>SDK: activate(activity, activationCode)
    SDK-->>W: Back to the wallet with the code
    Note over W,UDT: On any failure: declineActivation(activity, DECLINE)

The card_id and secret go into the SDK's card data parameters, built with MppCardDataParameters.withCardSecret(cardId, cardSecret) on Android, exactly as for push provisioning. The SDK calls below are all on MeaPushProvisioning.GooglePay or MeaPushProvisioning.ApplePay, except the iOS pass lookups, which are on MeaPushProvisioning itself.

StepAndroidiOSReact Native
Read the hand-offIntent EXTRA_TEXT; getTokenInfo(intent, listener) parses itappLaunchURL query: serialNumber, passTypeIdentifier, action=verifyThe Intent arrives as a deep link; then GooglePay.getRegisteredTokens()
Find the pending tokengetRegisteredTokens(...) or checkWalletForCardSuffix(lastFour, ...), state TOKEN_STATE_NEEDS_IDENTITY_VERIFICATIONsecureElementPassWithSerialNumber(serialNumber) and remoteSecureElementPassWithSerialNumber(serialNumber) for the Watch, state requiresActivationGooglePay.getRegisteredTokens(); ApplePay.secureElementPassWithSerialNumber(serialNumber)
Get the activation codegetActivationCode(cardParams, context, listener)getActivationData(cardParams), returns activation data rather than a codeGooglePay.getActivationCode(cardData); ApplePay.getActivationData(cardData)
Hand back to the walletactivate(activity, activationCode)activate(secureElementPass, withActivationData:), once per pass foundGooglePay.activate(activationCode); ApplePay.activate(secureElementPass, activationData)
DeclinedeclineActivation(activity, ActivationResult.DECLINE)No decline call is documented; leave the pass untouched and it stays pendingGooglePay.declineActivation('declined')
Server-side activation instead of a codeactivate(tokenUniqueReference, paymentNetwork), then activate(activity)activateSecureElementPass(secureElementPass, paymentNetwork)GooglePay.activateWithTokenInfo(tokenInfo); ApplePay.activate(secureElementPass, paymentNetwork)

What the wallet hands your app

Google Pay, AndroidApple Pay, iOS
How your app is openedAn explicit Android Intent to the package name and action configured for your app. The action is conventionally <your package>.a2aYour app's custom URL scheme, the appLaunchURL configured for your pass
What identifies the tokenEXTRA_TEXT: a Base64-encoded JSON carrying tokenUniqueReference and accountPanSuffixQuery parameters passTypeIdentifier, action=verify and serialNumber
If your app is not installedThe wallet opens your Play Store page. After installing, the cardholder restarts from the walletThe wallet offers the other methods. Your SMS and email path remains the fallback
First thing your app checksgetCallingPackage() is com.google.android.gms. Anything else, abortThe launch came through your registered URL scheme with the parameters above

What your app does, in order

  1. Accept the hand-off. Declare the Intent filter (Android) or the URL scheme (iOS) so the wallet can open your app, and read the token reference it passes. The SDK can parse the Android EXTRA_TEXT for you.
  2. Verify the caller. Only the wallet may start this flow. On Android that is the calling package check above; on iOS, the URL scheme and parameters.
  3. Authenticate the cardholder with your standard login. This is the whole point of the route: the wallet is trusting your authentication in place of a code, so it must be the real one, not a cached session.
  4. Find the pending token and check it belongs to this cardholder. On Android the SDK lists the wallet's tokens; the one you want is in state TOKEN_STATE_NEEDS_IDENTITY_VERIFICATION and its last four digits match a card of the logged-in cardholder. On iOS, look the pass up by serialNumber, on the iPhone and on a paired Watch, and check its activation state is requiresActivation. Never activate a token whose card is not linked to the authenticated user, whatever the wallet passed you.
  5. Complete the activation. One of the two methods below.
  6. Return control to the wallet, with the result or with an explicit decline if any step above failed. The wallet finishes the provisioning and the token goes live.

For the later case, where the cardholder opens your app on their own, steps 1 and 2 do not apply: after login, look for pending tokens (step 4) and, if there is one, offer to activate it, showing which device it is on. Then continue from step 5.

Two ways to complete the activation

Activation code, build this one. Your app obtains a one-time activation code from the SDK and hands it back to the wallet, which submits it for validation. To produce that code the SDK needs the same card_id and secret pair that push provisioning uses, so your backend calls GenerateTimeBasedSecret exactly as described in Push Provisioning, with the same 60-second rule. If you build push provisioning, you have already built this half. On iOS the equivalent is activation data rather than a code; the SDK call differs, the inputs do not.

Server-side activation. Instead of a code, the token is activated directly: your app calls the SDK's server-side activation with the token reference, or your backend calls /token/activate with reason A, and your app then returns the cardholder to the wallet without a code. Tell us before relying on this method so it can be enabled and tested with you.

What arrives on your backend

MessageOn the app route
digitization.activationmethodsArrives as always, at the start. Return your contact methods as today
digitization.activationNot expected. Where a code is used, it is produced on your side through the SDK, so there is nothing for us to deliver
digitization.completeArrives when the token is activated. Notify the cardholder as always
🚧

Two details being confirmed. Whether your activationmethods response must list type 6, or whether the scheme adds your app from the product configuration; and which message, if any, you receive when your app declines the activation. Until confirmed, keep returning types 1 and 2, and treat a declined attempt as abandoned in your tracking.

What UnDosTres needs from you

The wallet only knows how to open your app because the scheme's product configuration says so, and UnDosTres maintains that configuration on your behalf. Send us, for each environment:

PlatformValues
AndroidPackage name (applicationId) and the Intent action, in the form <package>.a2a
iOSBundle identifier and the custom URL scheme your app registers for the hand-off

Nothing on this route can be tested until those values are configured, so send final values rather than placeholders.

📘

Exact SDK calls. The provisioning SDK documents the method names and sample code for each platform: Android, section App-to-App Verification; iOS, section In-App Verification; and React Native, which carries both. This page tells you what to build and why; those pages tell you which method to call.


Tracking an attempt end to end

A provisioning attempt is started by digitization.activationmethods and finished by either digitization.complete or digitization.event.Digitization_Exception.

Store every incoming message with the timestamp you received it. You will need that for three things:

  1. Correlating messages to one attempt. Use the Token Unique Reference where present, or the card, to tie the sequence together.
  2. Abandoned provisioning. A cardholder who starts and never finishes should be reminded. See Use Cases and Runbook.
  3. Support. When a cardholder calls to say "it didn't work", the stored sequence tells you exactly which step it stopped at.

Related


Did this page help you?