m2pfintech
API LibraryAft Pull

Reverse AFT Pull Transaction

Reverse a previously successful AFT pull transaction.

Requirements:

  • Must be submitted within 24 hours of original transaction
  • Only for Visa Direct AFT transactions
  • Provide original RRN (Retrieval Reference Number)
POST
/direct/pull/reversal

Authorization

BasicAuth
AuthorizationBasic <token>

Partner credentials (username/password) provisioned during onboarding

In: header

Request Body

application/json

originalTransactionId*string

The externalTransactionId of the original AFT pull transaction being reversed

originalRetrievalReferenceNumber*string

RRN (Retrieval Reference Number) from the original pull transaction response

senderPrimaryAccountNumber*string

Payer card PAN from the original pull transaction

Match^\d{13,19}$
reason?string

Reason code for the reversal. Common values:

  • DUPLICATE_TRANSACTION — Transaction was processed more than once
  • CUSTOMER_REQUEST — Customer requested reversal
  • FRAUD — Suspected fraudulent transaction
externalTransactionId*string

Unique external ID for this reversal transaction (must be different from original)

business*string

Partner entity identifier

networkType*string

Must be VISA — AFT reversals are Visa Direct only

Value in"VISA"
transactionType*string

Must be AFT_PULL_REVERSAL for AFT pull reversals

transactionOrigin?string

Channel/origin of the reversal request

Value in"API" | "WEB" | "MOBILE" | "POS"
productId?string

Product identifier (should match original transaction)

originalTransactionMcc?string

MCC from the original transaction (defaults to 6540 if not provided)

Default"6540"

Response Body

application/json

application/json

application/json

curl -X POST "https://secure.yappay.in/Yappay/direct/pull/reversal" \  -H "Content-Type: application/json" \  -d '{    "originalTransactionId": "EXT-M2P-250",    "originalRetrievalReferenceNumber": "601018633791",    "reason": "DUPLICATE_TRANSACTION",    "business": "M2P",    "networkType": "VISA",    "transactionType": "AFT_PULL_REVERSAL",    "externalTransactionId": "EXT-M2P-253",    "transactionOrigin": "API",    "senderPrimaryAccountNumber": "4012001037141112",    "productId": "GENERAL"  }'
{
  "result": {
    "actionCode": "00",
    "responseCode": "string",
    "retrievalReferenceNumber": "string",
    "errorMessage": "string"
  },
  "error": {
    "errorCode": "string",
    "shortMessage": "string",
    "detailMessage": "string",
    "fieldErrors": [
      "string"
    ],
    "networkHttpStatusCode": 0
  }
}
{
  "result": null,
  "error": {
    "errorCode": "VALIDATION_ERROR",
    "shortMessage": "Invalid request",
    "detailMessage": "recipientPan is required",
    "fieldErrors": [
      "recipientPan: must not be blank"
    ]
  }
}
{
  "result": null,
  "error": {
    "errorCode": "INTERNAL_ERROR",
    "shortMessage": "Internal server error",
    "detailMessage": "An unexpected error occurred. Please contact support."
  }
}

Pull Funds from Card (AFT) POST

Debit funds from a customer's card (Account Funding Transaction). **Visa Direct only.** > **⚠️ Important**: The response from this API indicates only whether the request was > accepted or a 3DS challenge is required. **Do not rely on this response to determine > final transaction status.** Always call > `GET /Yappay/txn-manager/v2/fetch/{externalTransactionId}` to confirm the final > `txnStatus` (e.g., `PAYMENT_SUCCESS`, `PAYMENT_FAILURE`) after the 3DS flow completes. ## 3DS Authentication Modes This endpoint supports two 3DS flows depending on who performs authentication: ### Mode 1 — Platform-Managed 3DS (`method: THREE_DS`) Submit the request with `authentication.method: "THREE_DS"` and **omit** `cavv`/`eci`. The platform initiates the 3DS challenge and returns an `htmlContent` field containing an HTML form that must be rendered in the customer's browser to complete authentication. Once the customer completes the challenge, resubmit the transaction with the CAVV/ECI received. ### Mode 2 — Partner-Provided CAVV/ECI Complete 3DS externally (via your own 3DS provider) and submit the resulting `cavv` and `eci` in the `authentication` block. Omit `method` or leave it empty. ## Token Types (`tokenType`) - `"01"` — M2P-generated card token (PCI DSS tokenized by M2P) - `"02"` — Network-generated card token (e.g., MDES/VTS network token) - `"03"` — Clear card number (partner is PCI DSS compliant) ## Use Cases - PPI/wallet loading - Prepaid card top-up - Account funding

Create Card Token (Frontend Only) POST

Tokenize a customer's card by submitting AES-encrypted card data directly from the **customer's browser or mobile app**. Returns an `altId` (M2P card token) that can be used in AFT pull and OCT push calls with `tokenType: "01"`. > ⚠️ **This call MUST originate from the customer's frontend (browser/app), NOT > from your backend server.** If your backend relays this call, card data enters > PCI scope on your systems, violating the non-PCI compliance boundary. **Use the `url` returned by `POST /bitUrl/v2/generateSharedSecret`** as the full endpoint for this request (it contains a signed session `key` query parameter). ### Encryption Flow (JavaScript example) ```js const { CryptoJS } = window; // load from cdnjs // Keys derived from the generateSharedSecret response const cvvAESKey = CryptoJS.SHA256(serverPublicKey); const payloadKey = CryptoJS.SHA256(sharedSecret); const iv = CryptoJS.enc.Hex.parse("00000000000000000000000000000000"); // 1. Encrypt CVV using serverPublicKey-derived key const encryptedCvv = CryptoJS.AES.encrypt( cvv, cvvAESKey, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString(); // 2. Build payload and encrypt the whole thing using sharedSecret-derived key const payload = { cardNumber: "4012001037141112", cardExpiry: "2027-12", cvv: encryptedCvv, // CVV is double-encrypted networkType: "VISA", business: "YOUR_BUSINESS_CODE", entityId: "1234567890" }; const encryptedReq = CryptoJS.AES.encrypt( JSON.stringify(payload), payloadKey, { iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 } ).toString(); ``` The `encryptedReq` string above is the value to submit in the request body. ### Token Lifecycle - Token state: `ACTIVE` → `CONSUMED` (after one successful transaction) or `EXPIRED` (after ~15 min TTL) - Tokens are **single-use** — each AFT/OCT transaction consumes the token