---
id: openID4vci-payment-wallet-attestation
title: Payment Authenticator
hide_title: false
description: Follow the steps to issue verifiable payment wallet attestations using OpenID4VCI, enabling secure and trusted payment authentication workflows.
sidebar_label: Payment Authenticator
keywords: [payment authenticator, payment wallet attestation, OpenID4VCI, Organisation Wallet, European Business Wallet, EUDI Wallet, PSD2, payment authentication]
slug: /openID4vci-payment-wallet-attestation/
---

> **Build this with an AI coding agent.** Install the iGrant.io Agent Skills, then ask your agent to build the integration:
>
> ```bash
> npx skills add L3-iGrant/skills
> ```


<head>
  <title>Issue payment wallet attestations with OpenID4VCI | iGrant.io DevDocs</title>
  <meta name="description" content="Follow the steps to issue verifiable payment wallet attestations using OpenID4VCI, enabling secure and trusted payment authentication workflows." />
</head>

import Link from '@docusaurus/Link';
import VideoPlayer from '@site/src/components/VideoPlayer';
import NoteBox from '@site/src/components/NoteBox';

This workflow implements [European Wallet Consortium (EWC) RFC007](https://github.com/EWC-consortium/eudi-wallet-rfcs/blob/main/payment-rfcs/ewc-rfc007-payment-wallet-attestation.md), as specified by the [EWC Payment Task Force](https://github.com/EWC-consortium/eudi-wallet-rfcs/blob/main/payment-rfcs/images/EWC-EUDI-Wallet-PaymentTaskForce.png).

It uses the [OpenID4VCI Dynamic Credential Request](/docs/eo-ow-issue-dynamic-credential-request.mdx) protocol to issue a Payment Authenticator  by authenticating the user with an identity document (e.g., PID, Passport, etc.). The following steps outline the process using OpenID4VCI as illustrated below:

![Issue Payment Authenticator - PWA](./images/ow-issue-payment-wallet-attestation-with-individual.png)

### Step 1: Get the API Key (Bank Issuer Admin)

To obtain your API key, please contact [support@igrant.io](mailto:support@igrant.io?subject=Request%20API%20Key). Once you have received your API key, enter it in the field below and click the **Set API Key** button to save it for future use.

import { ApiKeyManager } from '@site/src/components/ApiKeyManager';

<ApiKeyManager />

### Step 2: Create Webhook (Bank Issuer Admin)

To create a webhook, you can run the code block below using the **Run** button. In the request payload, you must provide your webhook URL and a secret key:

* **`<yourWebhookUrl>`**: This is the endpoint on your server that will receive real-time notifications about subscribed events, such as when a presentation is acknowledged (`openid.presentation.presentation_acked.v3`).
* **`<yourSecretKey>`**: This is a secret key that you create. It is used to secure your webhook endpoint by allowing you to verify that incoming requests are genuinely from the iGrant.io platform.

Alternatively, you can manually copy the code block and use it in the body of the API request available [here](/docs/openid4vc-api/config-create-webhook).

import Step2 from '@site/src/components/PaymentWalletAttestationUsecase/Step2';

<Step2
  initialJsonData={{
  "webhook": {
    "payloadUrl": "https://example.com/webhook",
    "contentType": "application/json",
    "secretKey": "<random secret value for verifying webhook origin>",
    "disabled": false,
    "skipSslVerification": false,
    "subscribedEvents": {
      "consentManagementWebhook": [],
      "digitalWalletWebhook": [
        "openid.presentation.presentation_acked.v3",
        "openid.credential.credential_acked"
      ]
    }
  }
}}
/>

### Step 3: Create Presentation Definition (Bank Issuer Admin)

To create a presentation definition for requesting proof, you can run the code block below using the **Run** button. Alternatively, you can manually copy the code block and use it in the body of the API request provided [here](/docs/openid4vc-api/config-digital-wallet-open-id-presentation-definition).

From the API response, copy the `presentationDefinitionId` for use in Step 5.

<NoteBox title="Info:">
In the example below, a national passport is used as the identification document. This can also be a EU Person Identification Data (PID) or any other similar identification as required by the Bank Issuer.
</NoteBox>

import Step3 from '@site/src/components/PaymentWalletAttestationUsecase/Step3';

<Step3
  initialJsonData={{
    label: 'ID Check with National Passport',
    presentationDefinition: {
      id: '7c94e62d-82c2-41d7-a649-6c204bf59d1c',
      input_descriptors: [
        {
          id: '7c94e62d-82c2-41d7-a649-6c204bf59d1c',
          purpose: "Share your passport details to link your wallet with your bank account and receive your connection confirmation.",
          format: {
            'dc+sd-jwt': {
              alg: ['ES256'],
            },
            'vp+sd-jwt': {
              alg: ['ES256'],
            },
          },
          constraints: {
            fields: [
              {
                path: ['$.vct'],
                filter: {
                  type: 'string',
                  contains: {
                    const: 'Passport',
                  },
                },
              },
              {
                path: ['$.firstName'],
              },
              {
                path: ['$.lastName'],
              },
              {
                path: ['$.personalNumber'],
              },
              {
                path: ['$.expiryDate'],
              },
              {
                path: ['$.nationality'],
              },
              {
                path: ['$.issuerAuthority'],
              },
            ],
            limit_disclosure: 'required',
          },
        },
      ],
    },
  }}
/>

<NoteBox title="Tip:" variant="tip">
Once a presentation definition is created, the <code>presentationDefinitionId</code> can be reused to issue multiple credentials (Step 5).
</NoteBox>

### Step 4: Create Credential Definition (Bank Issuer Admin)

To create a credential definition as per EWC RFC007, you can run the code block below using the **Run** button. Alternatively, you can manually copy the code block and use it in the body of the API request available [here](/docs/openid4vc-api/config-create-digital-wallet-open-id-credential-definition). From the API response, copy the `credentialDefinitionId` for use in Step 5.

import Step4 from '@site/src/components/PaymentWalletAttestationUsecase/Step4';

<Step4
  initialJsonData={{
    label: "Payment Authenticator",
    vct: "PaymentWalletAttestation",
    expirationInDays: 168,
    display: {
      description:
        "You requested that your bank account be linked to your wallet. By choosing to accept, you agree to add the confirmation certificate to your wallet.",
      backgroundImage: {
        uri: "https://demo-api.igrant.io/v2/onboard/image/67a3c594c160fb4839d3f1af/web",
      },
      textColor: "#000000",
    },
    claims: {
      additionalProperties: false,
      properties: {
        fundingSource: {
          limitDisclosure: false,
          properties: {
            aliasId: {
              description: "Unique identifier assigned for the funding source.",
              limitDisclosure: true,
              type: "string",
            },
            currency: {
              description: "Currency code (ISO 4217, e.g., EUR, USD).",
              limitDisclosure: true,
              pattern: "^[A-Z]{3}$",
              type: "string",
            },
            icon: {
              description: "URI to card logo.",
              limitDisclosure: true,
              type: "string",
            },
            iin: {
              description: "Issuer Identification Number (IIN).",
              limitDisclosure: true,
              pattern: "^[0-9]{6,8}$",
              type: "string",
            },
            panLastFour: {
              description: "Last four digits of the card number.",
              limitDisclosure: true,
              pattern: "^[0-9]{4}$",
              type: "string",
            },
            parLastFour: {
              description:
                "Last four characters of the Payment Account Reference (PAR).",
              limitDisclosure: true,
              maxLength: 4,
              type: "string",
            },
            scheme: {
              description: "Card scheme.",
              enum: [
                "Visa",
                "American Express",
                "Discover",
                "JCB",
                "Mastercard",
                "UnionPay",
              ],
              limitDisclosure: true,
              type: "string",
            },
            type: {
              description: "Type of funding source.",
              enum: ["card"],
              limitDisclosure: true,
              type: "string",
            },
          },
          required: [
            "aliasId",
            "currency",
            "iin",
            "panLastFour",
            "scheme",
            "type",
          ],
          type: "object",
        },
      },
      required: ["fundingSource"],
      type: "object",
    },
    credentialFormat: "dc+sd-jwt",
    supportRevocation: true,
  }}
/>;

<NoteBox title="Tip:" variant="tip">
Once a credential definition is created, the <code>credentialDefinitionId</code> can be reused to issue multiple credentials (Step 5).
</NoteBox>

### Step 5: Issue Credential (Bank Issuer)

The Bank Issuer issues the Payment Authenticator  in deferred mode after completing the necessary validations. Replace `<credentialDefinitionId>` and `<presentationDefinitionId>` with the actual IDs obtained in the previous steps. You can run the JSON code block below using the **Run** button.

After receiving the response, toggle the button provided to dynamically generate a QR code. The EUDI Wallet/Holder can then accept the credential offer using the Data Wallet (or any other EU Digital Identity Wallet) by either scanning the QR code or directly accessing the credential offer on their mobile device, such as via a browser.

import Step5 from '@site/src/components/PaymentWalletAttestationUsecase/Step5';

<Step5
  initialJsonData={{
    issuanceMode: 'Deferred',
    credentialDefinitionId: '<credentialDefinitionId>',
    presentationDefinitionId: '<presentationDefinitionId>',
  }}
/>

From the API response, copy the `credentialExchangeId` and the `credential.id` for use in Step 6. The `credential.id` corresponds to the `id` field from the `credentialDefinitions` array in the create credential definition response. **Note that the `credential.id` is not returned or required when using the DRAFT_11 credential format.**

Alternatively, you can manually copy the code block, update the placeholders, and use it in the body of the API request available [here](/docs/openid4vc-api/config-digital-wallet-open-id-issue-credential/). From the response received, copy the `credentialOffer` and generate a QR code using a QR code generator tool, or paste the response into our tool [here](/docs/devtools-qrcode-generator/). The EUDI Wallet/Holder can then accept the credential offer using the [Data Wallet](https://igrant.io/datawallet.html) (or any other EU Digital Identity Wallet) by either scanning the QR code or directly accessing the credential offer on their mobile device (e.g., in a browser).

<NoteBox title="Tip:" variant="tip">
Before scanning the QR code, ensure that the Holder Wallet has onboarded the user's identity credentials as required by banking guidelines, such as a national passport or Person Identification Data (PID).
</NoteBox>

### Step 6: Update Credential Offer (Bank Issuer)

Upon receiving the `openid.presentation.presentation_acked.v3` webhook event and completing the requisite identity mapping in the bank backend, update the credential offer by running the code block below using the **Run** button. In the request payload, be sure to set the `credential.id` to the `id` value from the `credentialDefinitions` array in the create credential definition response.

Alternatively, you can manually copy the code block, update the placeholders, and use the `credentialExchangeId` from the response as the path parameter in the API request available [here](/docs/openid4vc-api/config-update-digital-wallet-open-id-credential-history).

import Step7 from '@site/src/components/PaymentWalletAttestationUsecase/Step7';

<Step7
  initialJsonData={{
    credential: {
      claims: {
        fundingSource: {
          aliasId: "123e4567-e89b-12d3-a456-426614174000",
          currency: "EUR",
          icon: "https://storage.googleapis.com/igrant-api-images/bancatransilvaniacardart.png",
          iin: "414011",
          panLastFour: "8027",
          parLastFour: "2345",
          scheme: "Visa",
          type: "card",
        },
      },
    },
  }}
/>;

### Try It Yourself (With Demo Video)

Watch the below demo to learn Payment Authenticator issuance and try it out yourselves:

<VideoPlayer
  videoId="YCi-IEdHNRY"
  title="Payment Authenticator Demo"
  description="Learn how to issue Payment Authenticator using OpenID4VCI protocol with iGrant.io Digital Wallets."
  uploadDate="2024-01-01"
  duration="PT10M"
/>