> ## Documentation Index
> Fetch the complete documentation index at: https://developer.tazapay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# upi_inr_native_qr_flow

> Accept UPI payments via dynamic QR code using the `upi_inr_native` payment method.

## Overview

UPI (Unified Payments Interface) is a real-time mobile payment system developed by NPCI. The merchant displays a dynamic QR code; the customer scans it using any UPI-enabled app and authenticates with their UPI PIN. Funds settle instantly.

***

## Step 1: Create a Payin

Create a payin with `invoice_currency: INR`. The payin is created with status `requires_payment_method`.

```bash theme={null}
curl --request POST \
     --url https://service-sandbox.tazapay.com/v3/payin \
     --header 'accept: application/json' \
     --header 'authorization: Basic xxxxxxxXXXXxxxxxxxxxxxxxxxxx' \
     --header 'content-type: application/json' \
     --data '{
  "invoice_currency": "INR",
  "amount": 10000,
  "transaction_description": "Test"
}'
```

***

## Step 2: Confirm the Payin

Confirm the payin to generate a QR code. Status moves to `requires_action`.

Pass the following in `payment_method_details`:

| Field   | Subfield      | Type   | Required | Description              |
| ------- | ------------- | ------ | -------- | ------------------------ |
| `type`  |               | enum   | Yes      | Must be `upi_inr_native` |
| `items` |               | array  | Yes      | List of items            |
|         | `name`        | string | Yes      | Item name                |
|         | `description` | string | No       | Item description         |
|         | `amount`      | int64  | Yes      | Unit price               |
|         | `quantity`    | int64  | Yes      | Quantity                 |

```bash theme={null}
curl --request POST \
     --url https://service-sandbox.tazapay.com/v3/payin/pay_cttprok2ukmk385sbfm0/confirm \
     --header 'accept: application/json' \
     --header 'authorization: Basic xxxxxxxXXXXxxxxxxxxxxxxxxxxx' \
     --header 'content-type: application/json' \
     --data '{
  "customer_details": {
    "name": "Andrea Lark",
    "email": "andrea@example.com",
    "country": "IN"
  },
  "items": [
    {
      "name": "Software Service for Antivirus",
      "description": "Firewall Antivirus",
      "amount": 10000,
      "quantity": 1
    }
  ],
  "payment_method_details": {
    "type": "upi_inr_native"
  }
}'
```

<Accordion title="Combine Steps 1 & 2 into a single API call">
  Pass all parameters to the Create Payin endpoint along with `"confirm": true`.

  | Field     | Type    | Required | Description                    |
  | --------- | ------- | -------- | ------------------------------ |
  | `confirm` | boolean | Yes      | Confirms the payin on creation |

  ```bash theme={null}
  curl --request POST \
       --url https://service-sandbox.tazapay.com/v3/payin \
       --header 'accept: application/json' \
       --header 'authorization: Basic xxxxxxxXXXXxxxxxxxxxxxxxxxxx' \
       --header 'content-type: application/json' \
       --data '{
    "customer_details": {
      "name": "Andrea Lark",
      "email": "andrea@example.com",
      "country": "IN"
    },
    "items": [
      {
        "name": "Software Service for Antivirus",
        "description": "Firewall Antivirus",
        "amount": 10000,
        "quantity": 1
      }
    ],
    "confirm": true,
    "invoice_currency": "INR",
    "amount": 10000,
    "transaction_description": "test",
    "payment_method_details": {
      "type": "upi_inr_native"
    }
  }'
  ```
</Accordion>

***

## Step 3: Display the QR Code

The confirm response includes a `qr_code` field (Base64-encoded) inside `latest_payment_attempt_data`. Decode and render it for the customer to scan.

```json theme={null}
{
  "data": {
    "status": "requires_action",
    "latest_payment_attempt_data": {
      "qr_code": "MDAwMjAxMjY1ODAwMDlTRy5QQVlOT1cwMTAxMjAyMTMyMDExMjA3OTdSMDAxMDMwMTAwNDE0MjAyNDAyMDYxODQwMDk1MjA0MDAwMDUzMDM3MDI1NDA2MTAwLjAwNTgwMlNHNTkyM1JFRCBET1QgUEFZTUVOVCBQVEUgTFRENjAwOVNpbmdhcG9yZTYyMjAwMTE2UVIyMDI0MDIwNlM5OTdIODYzMDQyRjA3"
    }
  }
}
```

<Note>
  The QR code expires after **5 minutes**. If the customer does not pay within this window, re-confirm the payin (Step 2) to generate a new QR code.
</Note>

***

## Step 4: Handle Post-Payment Events

Tazapay sends webhooks to your configured endpoint.

| Event                    | Description                 | Next Steps                                |
| ------------------------ | --------------------------- | ----------------------------------------- |
| `payin.succeeded`        | Customer paid before expiry | Fulfill the order                         |
| `payment_attempt.failed` | QR code expired unpaid      | Re-confirm the payin to generate a new QR |

***

## Testing

| Scenario         | Action                                                                               |
| ---------------- | ------------------------------------------------------------------------------------ |
| Simulate success | Click **Simulate Success** on the `redirect_url` → receives `payin.succeeded`        |
| Simulate failure | Click **Simulate Failure** on the `redirect_url` → receives `payment_attempt.failed` |

***

## Refunds

`upi_inr_native` supports **partial refunds**.

<Tabs>
  <Tab title="Via Dashboard">
    Refer to the [refund guide](https://support.tazapay.com/how-do-i-request-a-refund-from-my-dashboard).
  </Tab>

  <Tab title="Via API">
    ```bash theme={null}
    curl --request POST \
         --url https://service-sandbox.tazapay.com/v3/refund \
         --header 'accept: application/json' \
         --header 'authorization: Basic xxxxxxxXXXXxxxxxxxxxxxxxxxxx' \
         --header 'content-type: application/json' \
         --data '{
      "payin": "pay_cmiiaamaq0pbt3fkadm0",
      "amount": 10000,
      "currency": "INR",
      "reason": "Customer Return"
    }'
    ```

    <Note>
      `amount` and `currency` are not required for a full refund.
    </Note>
  </Tab>
</Tabs>
