# Feedback Loop
source: https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md

Feedback Loop is a secure and standardized way for you to share payment outcome data for Open Finance Pay products so Mastercard can continuously refine risk models and reduce payment failures across the network.

This data can be submitted securely via an AWS S3 directory with optional file-level encryption using Mastercard-provided keys.

Feedback Loop currently supports the below products and will be extended to support other Open Finance products in the future.

* [Payment Success Indicator (PSI)](https://developer.mastercard.com/open-finance-us/documentation/products/pay/psi-tools/index.md) -- improves the accuracy of payment success predictions and reduces unauthorized fraud risk by learning from real transaction outcomes.
* [Account ACH Details](https://developer.mastercard.com/open-finance-us/documentation/products/pay/verification-money-transfer/index.md) -- reduces payment failures by identifying and correcting data quality issues based on returned ACH payment results.

Note: All data submitted through the Feedback Loop is protected using industry-standard protocols. Each partner has isolated access to a dedicated S3 directory, ensuring strict data segregation. Files are retained for 30 days post-processing and handled in accordance with Mastercard's enterprise security and compliance standards. Tip: We recommend sharing data at consistent intervals (monthly or bi-weekly) to ensure models stay current and product performance improves consistently.

### Benefits {#benefits}

Sharing data through Feedback Loop helps improve outcomes for you and your customers.

* **Reduce payment failures**: improved data accuracy lowers return rates, minimizes exception handling, and reduces related processing fees.
* **Enhance product performance**: shared data refines Mastercard's predictive models, improving the accuracy and reliability of products such as PSI, ACH, and AO+.
* **Shape future innovation**: your feedback directly informs product enhancements and the development of new solutions aligned with your needs.

## How It Works {#how-it-works}

### High-Level Workflow {#high-level-workflow}

1. Prepare a CSV data file following Mastercard's schema for the product - see [Submission Format](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#submission-format).
2. Upload the file to the designated `inbound` directory within the assigned AWS S3 directory.
   * File-level encryption using Mastercard-provided keys can **optionally** be applied - see [Encryption](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#encryption) on this page.
3. Mastercard retrieves, decrypts, validates, and processes the submitted data.
4. After processing, an acknowledgement file or an error report (if validation issues are detected) is made available in the `outbound` directory within the same S3 directory. Monitor this directory to retrieve output files.

Diagram feedback-loop-normal

## Onboarding {#onboarding}

Contact your Customer Success Manager to start using Feedback Loop.

Tell your Customer Success Manager whether or not you want to enable encryption. See [Encryption](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#encryption) for details.

### S3 Configuration {#s3-configuration}

To enable access to your organization's dedicated directory within our S3 bucket, Mastercard will provide you with our AWS Account ID and the S3 bucket name.

Using this information, your team must:

* create an AWS IAM Role in your AWS account
* share the IAM Role ARN with Mastercard

<br />

This enables us to grant you access to your dedicated directory.

Your dedicated prefix is: `s3://<mastercard-bucket>/<your-namespace>/feedback-loop/`

Under this prefix we create two folders:

* `inbound/` -- to upload your data files to Mastercard
* `outbound/` -- to download acknowledgements and error reports from Mastercard

<br />

Our buckets have **ACLs disabled** . Do not set ACLs or send `x-amz-acl` headers in `PutObject` requests. Requests that try to read or set ACLs will be denied.

Our bucket enforces SSE‑KMS. Ensure your IAM role used for transfers has the following KMS actions on Mastercard's KMS key ARN:
`kms:GenerateDataKey`, `kms:Encrypt`, `kms:Decrypt`, `kms:ReEncrypt*`, `kms:DescribeKey`.

If your AWS organization uses **Service Control Policies (SCPs)**, verify they allow the above actions for our AWS account and KMS key ARNs.

## Submission Format {#submission-format}

This is the format for data you want to submit to Mastercard by uploading to the S3 directory.

All files must be in CSV format and
contain no more than 100,000 data rows per file.

Each file contains a two-row header, followed by [product-specific data](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#product-specific-data-requirements). The product-specific data format depends on whether you are submitting data for Payment Success Indicator or Account ACH.

Use a separate file for each product.

This is an example of the beginning of a submission data file for PSI:

```csv
fileName,parentPartnerId,dateTime,recordCount,startDate,endDate,product
Bulk_Feedback_01012025235959.csv,123456789,2025-01-01T23:59:59:999Z,2,12/31/2024,1/1/2025,psi
partnerId,customerId,payReqId,paymentId,settled,initiationAmount,initiationDate,settledDate,returnDate,returnReason
123456789,123456789,123456789,942b6870-461e-4d2e-80dd-a4672fd61b99,Y,10,12/31/2024,1/1/2025,,
123456789,123456789,123456789,572b6870-481d-7z2f-19eg-b1122gd81b45,N,10,12/31/2024,,1/1/2025,R20
...
```

### File Naming {#file-naming}

The filename must be in the form `Bulk_Feedback_mmddyyyyHHMMSS.csv`.
Warning: You must name the file correctly. Files that do not match `^Bulk_Feedback_\d{8,14}\.csv$` will not be processed.

### Header Requirements {#header-requirements}

* The first row of the file must contain the header field names (comma-separated).
* The second row must contain the corresponding header field values.
* All required header fields must be listed in row 1, with a corresponding value provided in row 2.
* Optional header fields may be omitted entirely, or listed in the header row but with no value provided.

#### Header Example {#header-example}

This is an example of the header rows showing only the required fields.

```csv
  fileName,dateTime,recordCount,product
  "Bulk_Feedback_01012025235959.csv","2025-01-01T23:59:59Z","2","psi"
```

#### Header Fields {#header-fields}

|    Field Name     |   Type   |                              Description                               | Required |              Example               |
|-------------------|----------|------------------------------------------------------------------------|----------|------------------------------------|
| `fileName`        | string   | Name of the file (`Bulk_Feedback_mmddyyyyHHMMSS.csv`)                  | Y        | `Bulk_Feedback_01012025235959.csv` |
| `parentPartnerID` | integer  | Only used if required to track relationships between partner companies | N        | `12345`                            |
| `dateTime`        | datetime | Date \& time the file is generated (ISO 8601)                          | Y        | `2025-01-01T23:59:59Z`             |
| `recordCount`     | integer  | Number of individual records in the file                               | Y        | `2`                                |
| `startDate`       | datetime | Start date of transactions included (ISO 8601)                         | N        | `2025-01-01`                       |
| `endDate`         | datetime | End date of transactions included (ISO 8601)                           | N        | `2025-01-31`                       |
| `product`         | string   | Open Finance product this file relates to (supported: `ach`, `psi`)    | Y        | `psi`                              |

<br />

### Product-Specific Data Requirements {#product-specific-data-requirements}

Each product has its own schema with a defined set of required and optional fields for the product-specific data rows.
Warning: `partnerId` is a required field for all products. Make sure you provide the partner ID of your **Production** project, not a Sandbox project. To find the correct partner ID, go to your [Mastercard Developers Dashboard](https://developer.mastercard.com/dashboard), select your **Production** project and go to **Credentials \> Production**.

If any required fields for a given product
are missing in a row, the whole submission is rejected.
Ensure that all required fields are populated according to the
product schema.

The schemas for each product are detailed in the following sections.

#### Payment Success Indicator (PSI) Fields {#payment-success-indicator-psi-fields}

|     Field Name     | Data Type |                        Description                         | Required |                             Notes                             |
|--------------------|-----------|------------------------------------------------------------|----------|---------------------------------------------------------------|
| `partnerId`        | integer   | Production partner ID provided by Mastercard               | Y        | Must exist in system                                          |
| `customerId`       | integer   | Customer ID provided by Mastercard                         | Y        | Must exist in system                                          |
| `payReqId`         | string    | ID of the payment transaction this feedback relates to     | Y        | Use payment request ID (`payReqId`) from earlier PSI response |
| `paymentId`        | string    | Partner-provided identifier to track/update payment status | Y        | Helps correlate multiple submissions (e.g. retries)           |
| `settled`          | string    | Indicates whether the transaction settled                  | Y        | Values: `Y` or `N`                                            |
| `initiationAmount` | decimal   | Value of the transaction                                   | Y        | Digits or decimal                                             |
| `initiationDate`   | string    | Date the transaction was initiated                         | Y        | Provide actual initiation timestamp                           |
| `settledDate`      | string    | Date the transaction settled                               | Y        | Leave blank if `settled = N`                                  |
| `returnDate`       | string    | Date the transaction was returned (yyyy-mm-dd)             | Y        | Leave blank if `settled = Y`                                  |
| `returnReason`     | string    | Reason the transaction was returned (e.g. ACH return code) | Y        | Leave blank if `settled = Y`                                  |

Example PSI input file:
[PSI-BulkFeedbackLoop-Example.csv](https://static.developer.mastercard.com/content/open-finance-us/uploads/loop/PSI-BulkFeedbackLoop-Example.csv) (476B)

#### Account ACH Details Fields {#account-ach-details-fields}

|      Field Name      | Data Type |                        Description                         | Required |            Notes            |
|----------------------|-----------|------------------------------------------------------------|----------|-----------------------------|
| `partnerId`          | integer   | Production partner ID provided by Mastercard               | Y        | Must exist in system        |
| `customerId`         | integer   | Customer ID provided by Mastercard                         | Y        | Must exist in system        |
| `accountId`          | string    | Account ID assigned by Mastercard                          | Y        | Must match existing record  |
| `accountNumberLast4` | string    | Last 4 digits of account number used to initiate payment   | N        | Example: `1234`             |
| `routingNumber`      | string    | Routing number used to initiate payment                    | N        | Example: `091000019`        |
| `paymentId`          | string    | Partner-provided identifier to track/update payment status | Y        | Useful for retries/updates  |
| `settled`            | string    | Indicates whether the transaction settled                  | Y        | Values: `Y` or `N`          |
| `initiationAmount`   | decimal   | Value of the transaction                                   | N        | Digits or decimal           |
| `initiationDate`     | string    | Date the transaction was initiated                         | Y        | Actual initiation timestamp |
| `settledDate`        | string    | Date the transaction settled                               | N        | Blank if `settled = N`      |
| `returnDate`         | string    | Date the transaction was returned (YYYY-MM-DD)             | Y        | Blank if `settled = Y`      |
| `returnReason`       | string    | Reason the transaction was returned (e.g. ACH return code) | Y        | Blank if `settled = Y`      |

Example ACH input file:
[ACH-BulkFeedbackLoop-Example.csv](https://static.developer.mastercard.com/content/open-finance-us/uploads/loop/ACH-BulkFeedbackLoop-Example.csv) (538B)

## Acknowledgement Format {#acknowledgement-format}

This is the format of files returned by Mastercard indicating processing status and any detected errors.

If `errorCount` \> `0`, the file has failed validation. You must correct the errored records in the original file and resubmit the entire file.
Alert: If the acknowledgement file has any errors, none of the records in the submission were processed. You must resubmit the entire file with corrections. Partial resubmissions (sending only the corrected records) are not supported.

See [Error Handling and Feedback](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#error-handling-and-feedback) below for details of file format error codes.

If you have chosen to enable encryption, you will need to decrypt the acknowledgement file - see [Decrypting Data](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#decrypting-data).

|       Field Name       |   Type   |                               Description                               | Required |                      Example                       |
|------------------------|----------|-------------------------------------------------------------------------|----------|----------------------------------------------------|
| `fileName`             | string   | Name of the acknowledgement file                                        | Y        | `Bulk_Feedback_Acknowledgement_01012025001500.csv` |
| `receivedFileName`     | string   | Original inbound file name                                              | Y        | `Bulk_Feedback_01012025235959.csv`                 |
| `receivedFileDateTime` | datetime | Date/time original file was received (ISO 8601)                         | Y        | `2025-01-01T23:59:59Z`                             |
| `successCount`         | integer  | Count of successfully processed records                                 | N        | `200`                                              |
| `errorCount`           | integer  | Count of records with errors (0 = no errors; \>0 requires resubmission) | N        | `5`                                                |
| `totalCount`           | integer  | Total number of records received                                        | N        | `205`                                              |
| `fileFormatErrorCode`  | string   | File format error code (if applicable)                                  | N        | `ERR-101`                                          |
| `fileFormatErrorDesc`  | string   | Description of the file format error                                    | N        | `Missing required field: recordCount`              |
| `ackTimestamp`         | datetime | Date/time the acknowledgement was generated (UTC ISO 8601)              | Y        | `2025-01-02T00:15:00Z`                             |

Sample acknowledgement files:

* Success: [Bulk_Feedback_acknowledgement_20250902124518-success.csv](https://static.developer.mastercard.com/content/open-finance-us/uploads/loop/Bulk_Feedback_acknowledgement_20250902124518-success.csv) (281B)
* Errors: [Bulk_Feedback_acknowledgement_20250902161006-error.csv](https://static.developer.mastercard.com/content/open-finance-us/uploads/loop/Bulk_Feedback_acknowledgement_20250902161006-error.csv) (1KB)

## Error Handling and Feedback {#error-handling-and-feedback}

This table contains the errors that may be returned by Mastercard in an
acknowledgement file.

If you receive validation errors, correct the affected records
and resubmit the entire file.
Alert: Feedback data is not ingested or processed unless the whole file has passed validation with no errors.

| Error Code |                                                                                                                                                                                                                                Error Description                                                                                                                                                                                                                                |                                                                         Action to be taken                                                                         |
|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `450001`   | File format error in file \<filename\>. Unable to read or parse the file. Expected file format: CSV                                                                                                                                                                                                                                                                                                                                                                             | Fix the error and resend the entire file.                                                                                                                          |
| `450002`   | Missing required field(s): \<fieldnames\> in line \<line number\> This error can occur on multiple field names in a line item. Please verify.                                                                                                                                                                                                                                                                                                                                   | Include the mandatory fields and resend the entire file.                                                                                                           |
| `450003`   | \<fieldname\> has invalid data in line \<linenumber\>. The \<fieldname\> should be numeric OR \<fieldname\> has invalid data in line \<linenumber\>. The \<fieldname\> should not be future date. OR \<fieldname\> has invalid data in line \<linenumber\>. The \<fieldname\> should be boolean For example: - `settled` indicator must be 'Y' or 'N' - `initiatedAmount` should be numeric and can have up to two decimal places - `settledDate` must follow YYYY-MM-DD format | Correct the field value and resend the entire file. The error can occur on multiple fields in a line item, so check all fields.                                    |
| `450004`   | No record found in our system for partner: \<partnerId\> in line \<linenumber\> OR No match found for partner: \<partnerId\>, customer: \<customerId\>, account: \<accountId\> combination in line \<linenumber\> The combination of `partnerId`, `customerId` and `accountId` must match a valid record                                                                                                                                                                        | Fix the failed record and resend the entire file.                                                                                                                  |
| `450005`   | Required file(s) missing - provide .bin and metadata.json files                                                                                                                                                                                                                                                                                                                                                                                                                 | Provide both a .bin file and a .metadata.json file in the zip archive.                                                                                             |
| `450006`   | Unable to decrypt .bin file in zip                                                                                                                                                                                                                                                                                                                                                                                                                                              | Make sure that: - the .bin file is encrypted - the .metadata.json file is valid JSON and contains encryption key data that was provided via Mastercard Developers. |

## Encryption {#encryption}

For additional security, file-level encryption may be applied using Mastercard-provided keys.
Note: Encryption is **optional**.

Enabling encryption is an all-or-nothing setting --- it is either enabled or disabled for all Feedback Loop uploads.

Contact your Customer Success Manager if you want to enable encryption.

Feedback Loop supports **symmetric AES‑256 with JWE metadata** only. PGP/GPG is not supported.

To use encryption, you must have a project in Mastercard Developers with Production access and encryption key generation enabled.

Tell your Customer Success Manager the project's partner ID. To find the partner ID, go to your [Mastercard Developers Dashboard](https://developer.mastercard.com/dashboard), select your **Production** project and go to
**Credentials \> Production**.

If encryption is enabled:

* you must encrypt all your submission files.
* instead of providing a single CSV, you must provide a ZIP containing an encrypted `.bin` file with JSON metadata.
* acknowledgement files from Mastercard are encrypted.

<br />

| Encryption toggle |       What you upload to `inbound/`        |                 What you receive in `outbound/`                 |
|-------------------|--------------------------------------------|-----------------------------------------------------------------|
| Disabled          | A single CSV (`Bulk_Feedback_*.csv`)       | A single acknowledgement CSV                                    |
| Enabled           | A ZIP containing `.bin` + `.metadata.json` | An encrypted ZIP with `*_success.bin` / `*_fail.bin` + metadata |

<br />

Diagram feedback-loop-encrypt

### Encryption Keys {#encryption-keys}

Data must be encrypted with two keys:

* the Client Encryption Key (generated by your organization).
* the Mastercard Encryption Key for your Production project (provided by Mastercard).

<br />

You can create keys from the [Mastercard Developers Dashboard](https://developer.mastercard.com/dashboard).
Note: Key creation is not enabled by default. If you do not see the option, ask your Customer Success Manager to enable it.

Once created, you can view and download the keys from the **Credentials** section when viewing your Production project details.

![](https://static.developer.mastercard.com/content/open-finance-us/uploads/mcd-keys-prod-keys-openfinance.png)

### Encrypting Data {#encrypting-data}

There are two aspects to the encryption process:

* You will need to encrypt your CSV data file using an AES-256 ephemeral key.
* Then you need to encrypt your AES-256 ephemeral key, IV byte array, and encryption tag using JSON Web Encryption (JWE). This should then be packaged as a metadata file in JSON format.

The resulting two files should then be placed in a zip and sent to Mastercard via S3.

Follow the steps below to securely send Feedback Loop data to Mastercard:

1. Prepare the submission data in a CSV file (see [Submission Format](https://developer.mastercard.com/open-finance-us/documentation/products/pay/feedback-loop/index.md#submission-format) on this page).

2. Create an initial vector (IV) byte array and an ephemeral AES 256-bit key as a data encryption key. When creating this key, use the AES algorithm in GCM mode (AES/GCM/NoPadding). You can find sample code for creating both an IV byte array and an AES ephemeral key on GitHub [here](https://github.com/Mastercard/client-encryption-java/blob/main/src/main/java/com/mastercard/developer/encryption/aes/AESEncryption.java#L28).

3. Encrypt the CSV file using the ephemeral key and IV byte array from step 2, with the AES encryption algorithm in GCM mode. Name this file using the naming convention `Bulk_Feedback_mmddyyyyHHMMSS.bin`. The encryption result will also return a `tag` value that will be used in the next step. The following python example illustrates this:

* Python

```python
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes

key = get_random_bytes(32) # 32 bytes X 8 bits = 256 bits
iv = get_random_bytes(16)
cipher = AES.new(key, AES.MODE_GCM, nonce=iv, use_aesni=True)

blocksize = 64 * 1024 * 1024 # 64 MB block

with open("path/to/raw/Bulk_Feedback_mmddyyyyHHMMSS.csv", 'rb') as reader, \
    open("path/to/encrypted/Bulk_Feedback_mmddyyyyHHMMSS.bin", 'wb') as writer

# encrypt large file in 64 MB chunk. This is to avoid loading entire file in memory
while True:
    block = reader.read(blocksize)
    if not block:
    break
    writer.write(cipher.encrypt(block))

tag = cipher.digest()
```

4. Mastercard will need to know how to decrypt your encrypted CSV file. To do this you need to send the necessary details within a JWE. Encode the `key`, `iv`, and `tag` values as base64 encoded strings (with ASCII codec), and create a JSON metadata payload in the following format:

   ```json
   {
   "key": "5gO+78x1ZjKG3BzwY8AP9dovzUyrBtL/HNgrVIggEIw=",
   "iv": "9PYO6dNmmTuchtbpwT7ldA==",
   "tag": "+vEElaaYP05RMAvR42QEHw=="
   }
   ```

   Encrypt this metadata with JWE Encryption. You can use the [Mastercard Client Encryption Library](https://github.com/Mastercard?&q=client-encryption) to do so. You will need to use your Client Encryption Key tied to your project when setting up the JWE configuration.

* Python

```python
import json
from client_encryption.jwe_encryption_config import JweEncryptionConfig
from client_encryption.jwe_encryption import encrypt_payload, decrypt_payload

metadata = {
"key": "5gO+78x1ZjKG3BzwY8AP9dovzUyrBtL/HNgrVIggEIw=",
"iv": "9PYO6dNmmTuchtbpwT7ldA==",
"tag": "+vEElaaYP05RMAvR42QEHw=="
}

payload = {
"enc": metadata,
}

pub_cert_path = "path/to/client-encryption-key"
conf = JweEncryptionConfig({
"paths": {
    "$": {
    "toEncrypt": {
        "enc": "enc",
    },
    "toDecrypt": {}
    }
},
"encryptedValueFieldName": "jwe",
"encryptionCertificate": pub_cert_path
})

enc = encrypt_payload(payload=payload, config=conf)
jwe = enc["enc"]

with open("path/to/Bulk_Feedback_mmddyyyyHHMMSS.metadata.json") as metaf:
metaf.write(json.dumps(jwe))
```

5. The JWE created will be used in the next step.

6. Package the returned JWE into a JSON file called `Bulk_Feedback_mmddyyyyHHMMSS.metadata.json` in the following form:

* Json

```json
{
"jwe": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZTI0YzAyZDU0NGIyYjExNmJhMDZlZDc2NWNjZDY1MzI3OGZkNmIxODQ2MTAxZTU0MjdlNjM2YzkwY2RlZjk0OSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24ifQ.YbgsnILXFkVba7mU0PUQpMwsWuVASTAfTIeokghGnCgQbLpjKag75slXCQyroJ27fK0Y_Qgus05yWKYKRaP6zdP1YO5mLIm44iz-EPpz1e3v6DgkZ5MMTxeMqREPkC03cTO9OIajIw0UgbafrmfJ18izJojKerdsnMvnEKZ8_23oqMYN7CHW9Jg5HuEaqxKme0OoITJpzs4Jk-bPsvtxeWam8iNH9l77hWuf2wyTIW_qyx5J9vBXNB6_96uTCtHECttOQkk7-Tk1zYmhKqR4ZG8AOMTIE4YHFmkQoSLxU8YAuMDQFN4Xr5egoo9AlUkN7urrf37gdgUG1ldPVxgXcg.EjRNhXhJUj50-q9DB7XSkg.4fGc5xCuoySNzbMhDYUGRdEA2FfRqlV5yEdrDMlu3sz3tHycLrLJXYBaPyueCUWsi2RJpG85-nY-o689Z2TOQkfu9UZty8nyLfmFfhpUGwhcCaN-OZaXSYOxsLR2UIgbJ8VuFnml1dJ5BfswIaJF2UZXjfbA6yNHig4Jvg.aPVGN0J-JUfqzYhuQ_pkFA"
}
```

7. Package the `Bulk_Feedback_mmddyyyyHHMMSS.bin` and `Bulk_Feedback_mmddyyyyHHMMSS.metadata.json` into a zip folder using this naming convention: `Bulk_Feedback_mmddyyyyHHMMSS.zip`

8. Transfer the zip package to Mastercard via S3 (in the `/inbound` directory).

Warning: Make sure you name the files as specified --- otherwise, the upload will not be processed.

### Decrypting Data {#decrypting-data}

Downloading and decrypting the acknowledgement file is the reverse of the process above for sending files. You will need to download a zip, extract the JWE from the metadata file, and use the ephemeral encryption key details from the JWE to then decrypt the output file itself. There may be two output files, as any failures will be returned in a second file along with error messages.

1. Transfer the output file from the Mastercard S3 directory to your network. The file will be named with this naming convention: `Bulk_Feedback_mmddyyyyHHMMSS_output.zip`

2. Unzip the files:

   * `Bulk_Feedback_mmddyyyyHHMMSS.metadata.json` - This file contains the encryption metadata.
   * `Bulk_Feedback_mmddyyyyHHMMSS_success.bin` - This is an encrypted file which contains valid records.
   * `Bulk_Feedback_mmddyyyyHHMMSS_fail.bin` - This is an encrypted file which contains error details. Fix the errors and resubmit the entire file.

   The metadata JSON file contains two fields, `jwe` and `fingerprint`:
   * `jwe` contains the encrypted `key` and `iv` used during the encryption of the output files, plus the `successTag` and `failTag` extracted from encrypted output files.
   * `fingerprint` contains the fingerprint of the key pair used for encrypting the output files.

   For example:
   * Json

   ```json
       {
          "jwe": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZTI0YzAyZDU0NGIyYjExNmJhMDZlZDc2NWNjZDY1MzI3OGZkNmIxODQ2MTAxZTU0MjdlNjM2YzkwY2RlZjk0OSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24ifQ.YbgsnILXFkVba7mU0PUQpMwsWuVASTAfTIeokghGnCgQbLpjKag75slXCQyroJ27fK0Y_Qgus05yWKYKRaP6zdP1YO5mLIm44iz-EPpz1e3v6DgkZ5MMTxeMqREPkC03cTO9OIajIw0UgbafrmfJ18izJojKerdsnMvnEKZ8_23oqMYN7CHW9Jg5HuEaqxKme0OoITJpzs4Jk-bPsvtxeWam8iNH9l77hWuf2wyTIW_qyx5J9vBXNB6_96uTCtHECttOQkk7-Tk1zYmhKqR4ZG8AOMTIE4YHFmkQoSLxU8YAuMDQFN4Xr5egoo9AlUkN7urrf37gdgUG1ldPVxgXcg.EjRNhXhJUj50-q9DB7XSkg.4fGc5xCuoySNzbMhDYUGRdEA2FfRqlV5yEdrDMlu3sz3tHycLrLJXYBaPyueCUWsi2RJpG85-nY-o689Z2TOQkfu9UZty8nyLfmFfhpUGwhcCaN-OZaXSYOxsLR2UIgbJ8VuFnml1dJ5BfswIaJF2UZXjfbA6yNHig4Jvg.aPVGN0J-JUfqzYhuQ_pkFA",
          "fingerprint": "cfb9bf8efec58af5f5231d73d7785f2eb3991c3dae8b1539a3cde9fdb6cd6dab"
       }
       
   ```

3. Extract the JWE from the metadata and decrypt it to obtain the ephemeral key that was used by Mastercard to encrypt the processed file. You can use the Mastercard Client Encryption Library to do so with JWE decryption. Use the Mastercard Decryption Key associated with `fingerprint` to decrypt the `jwe` value.

4. Decrypting the JWE should produce a JSON file containing `key`, `iv`, `successTag` and `failTag`. These will be base64 encoded strings (with ASCII codec). Decode them to bytes. You will then be able to use these to decrypt the data files.

5. With AES in GCM mode and the `key` and `iv` from the previous step, decrypt the `.bin` files:

   * To decrypt the success file, use `Bulk_Feedback_mmddyyyyHHMMSS_success.bin` as the payload and the `success_tag` as the tag.
   * To decrypt the fail file, use `Bulk_Feedback_mmddyyyyHHMMSS_fail.bin` as the payload and the `fail_tag` as the tag.

The following Python code example shows the decryption of the JWE and then the data files.
* Python

```python
import base64

enc_metadata = {
  "jwe": "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoiZTI0YzAyZDU0NGIyYjExNmJhMDZlZDc2NWNjZDY1MzI3OGZkNmIxODQ2MTAxZTU0MjdlNjM2YzkwY2RlZjk0OSIsImN0eSI6ImFwcGxpY2F0aW9uL2pzb24ifQ.YbgsnILXFkVba7mU0PUQpMwsWuVASTAfTIeokghGnCgQbLpjKag75slXCQyroJ27fK0Y_Qgus05yWKYKRaP6zdP1YO5mLIm44iz-EPpz1e3v6DgkZ5MMTxeMqREPkC03cTO9OIajIw0UgbafrmfJ18izJojKerdsnMvnEKZ8_23oqMYN7CHW9Jg5HuEaqxKme0OoITJpzs4Jk-bPsvtxeWam8iNH9l77hWuf2wyTIW_qyx5J9vBXNB6_96uTCtHECttOQkk7-Tk1zYmhKqR4ZG8AOMTIE4YHFmkQoSLxU8YAuMDQFN4Xr5egoo9AlUkN7urrf37gdgUG1ldPVxgXcg.EjRNhXhJUj50-q9DB7XSkg.4fGc5xCuoySNzbMhDYUGRdEA2FfRqlV5yEdrDMlu3sz3tHycLrLJXYBaPyueCUWsi2RJpG85-nY-o689Z2TOQkfu9UZty8nyLfmFfhpUGwhcCaN-OZaXSYOxsLR2UIgbJ8VuFnml1dJ5BfswIaJF2UZXjfbA6yNHig4Jvg.aPVGN0J-JUfqzYhuQ_pkFA",
  "fingerprint": "cfb9bf8efec58af5f5231d73d7785f2eb3991c3dae8b1539a3cde9fdb6cd6dab"
}

jwe = enc_metadata["jwe"]
fingerprint = enc_metadata["fingerprint"]

# get the private key (Mastercard Encryption Key) associated with fingerprint
private_key_file = "path/to/certs/keyname-mastercard-encryption-key.p12"

payload = {
  "enc": jwe,
}

cfg = {
  "paths": {
    "$": {
      "toEncrypt": {},
      "toDecrypt": {
        "enc.jwe": "dec",
      }
    }
  },
  "encryptedValueFieldName":"jwe",
  "decryptionKey": private_key_file,
  "decryptionKeyPassword": "<your-passphrase>"
}

conf = JweEncryptionConfig(cfg)
metadata = decrypt_payload(payload=payload, config=conf)

key = base64.b64decode(metadata["key"])
iv = base64.b64decode(metadata["iv"])
tagSuccess = base64.b64decode(metadata["tagSuccess"])
tagFail = base64.b64decode(metadata["tagFail"])


def decrypt_file(key: bytes, nonce: bytes, enc_file: str, dec_file: str, tag: bytes):
    blocksize = 64 * 1024 * 1024 # 64 MB block

     with open(enc_file, 'rb') as reader, open(dec_file, 'wb') as writer:
        cipher = AES.new(key, AES.MODE_GCM, nonce=nonce, use_aesni=True)

        # decrypt large file in 64 MB chunk. This is to avoid loading entire file in memory
        while True:
            block = reader.read(batch_size)
            if not block:
                break
            writer.write(cipher.decrypt(block))
        cipher.verify(tag)


enc_file = f"path/to/Bulk_Feedback_mmddyyyyHHMMSS_success.bin"
dec_file = f"path/to/Bulk_Feedback_mmddyyyyHHMMSS_success.csv"
decrypt_file(key, iv, enc_file, dec_file, tagSuccess)

enc_file = f"path/to/Bulk_Feedback_mmddyyyyHHMMSS_fail.bin"
dec_file = f"path/to/Bulk_Feedback_mmddyyyyHHMMSS_fail.csv"
decrypt_file(key, iv, enc_file, dec_file, tagFail)
```

