# Inovio Developer (complete)
Built 2026-09-29. Source pages: https://developer.inoviopay.com/llms.txt
---
# Inovio Developer
Inovio is a global payment gateway. Integrate with the Gateway API, a server-side SDK in PHP, Node, Python or Java, or a plugin for WooCommerce, Magento 2, PrestaShop 9 or OpenCart 4.
Source: https://developer.inoviopay.com/index.html
Markdown: https://developer.inoviopay.com/index.md
Inovio: your global payment gateway.
Secure, intelligent processing with global scalability. Flexible APIs, four server-side SDKs and ready-made shopping cart plugins make the integration fast, and every page here is also plain Markdown so your AI assistant can read it too.
Full currency conversion and intelligent transaction routing for global eCommerce. Request a transaction in one currency and read back the settled currency and exchange rate.
Every page has a Markdown twin at the same address with a `.md` extension. The OpenAPI document describes the form-encoded API, and the Postman collection is generated from every cURL example on this site.
## Source code
| Project | Repository |
|---|---|
| PHP SDK | https://github.com/Inoviopay/inovio-gateway-sdk-php |
| Node / TypeScript SDK | https://github.com/Inoviopay/inovio-gateway-sdk-node |
| Python SDK | https://github.com/Inoviopay/inovio-gateway-sdk-python |
| Java SDK | https://github.com/Inoviopay/inovio-gateway-sdk-java |
| WooCommerce plugin | https://github.com/Inoviopay/inovio-gateway-woocommerce |
| Magento 2 / Mage-OS module | https://github.com/Inoviopay/inovio-gateway-magento2 |
| PrestaShop 9 module | https://github.com/Inoviopay/inovio-gateway-prestashop |
| OpenCart 4 extension | https://github.com/Inoviopay/inovio-gateway-opencart |
---
# Get started
What you need from Inovio, how test and production work, and which integration path to pick.
Source: https://developer.inoviopay.com/get-started.html
Markdown: https://developer.inoviopay.com/get-started.md
## What you need from Inovio
Every integration path uses the same five values. Inovio support issues them when your account is boarded.
| Credential | Wire name | Used by |
|---|---|---|
| API username | `REQ_USERNAME` | API, SDKs, plugins |
| API password | `REQ_PASSWORD` | API, SDKs, plugins |
| Site ID | `SITE_ID` | API, SDKs, plugins |
| Site Key | (HMAC secret, never sent) | Tokenization: SDKs' `tokenize()`, and the plugins' browser direct-post |
| Gateway Product ID | `LI_PROD_ID` | Plugins (the product each order bills under) |
> **The Site Key is not the API password**
> It is a separate per-site HMAC secret used only to sign tokenization requests. Without it the token service answers error 121 and a plugin checkout cannot proceed. You cannot generate it yourself.
An optional **Merchant Account ID** (`MERCH_ACCT_ID`) pins transactions to one merchant account. Leave it out to let the gateway route by currency and country.
## Test and production are the same endpoint
There is no sandbox host. You use the same credentials and the same URLs for testing and for live processing. What changes is how your site is configured in the Inovio portal: during testing your site points at a Test Bank merchant ID, and when you go live Inovio repoints it at your production merchant IDs. No code change is needed.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
Verify credentials with `TESTAUTH` and gateway availability with `TESTGW`; both are on the [overview page](https://developer.inoviopay.com/api/overview.md). Test card numbers and the scripted decline scenarios are on [Testing](https://developer.inoviopay.com/api/testing.md).
## Pick a path
| If you… | Use | Time to first approved transaction |
|---|---|---|
| Run WooCommerce, Magento 2, PrestaShop 9 or OpenCart 4 | A [shopping cart plugin](https://developer.inoviopay.com/carts/index.md) | Minutes. Install, enter the five credentials, enable. |
| Have your own checkout in PHP, Node, Python or Java | A [server-side SDK](https://developer.inoviopay.com/sdks/index.md) | An hour. `client.sale(request)` and handle a five-state result. |
| Need something the SDKs do not cover yet (ACH, wallets, subscriptions) or use another language | The [Gateway API](https://developer.inoviopay.com/api/overview.md) directly | The API is one form-encoded POST; the SDK pages show the exact fields each method sends. |
## Where the card number goes
The gateway supports three ways the card number can reach it, from the most to the least card data on your server:
1. **Raw PAN to the API.** Your server sends `PMT_NUMB`. Simple, but your server handles card data.
2. **Server-side tokenization.** Your server calls the token service, gets a single-use `TOKEN_GUID`, and uses that instead of the PAN. The SDKs' `tokenize()` does this. The PAN still transits your server.
3. **Browser direct-post.** The shopper's browser posts the PAN straight to the token service, signed with an HMAC your server minted from the Site Key. Only the token reaches you. This is what all four cart plugins do.
A browser Hosted Fields library for custom checkouts is planned; until it ships, the plugins are the reference implementation of level 3. See [Tokenization](https://developer.inoviopay.com/api/tokenization.md).
## Next
- [Sale (CCAUTHCAP)](https://developer.inoviopay.com/api/sale.md): the one request most integrations start with.
- [How the SDKs think](https://developer.inoviopay.com/sdks/concepts.md): the five-state result, decimal money, idempotency.
- [Shopping cart plugins](https://developer.inoviopay.com/carts/index.md): the capability matrix and the shared refund design.
---
# Use cases
Find the integration that matches what you need to do, and jump to the pages and sample code that cover it.
Source: https://developer.inoviopay.com/use-cases.html
Markdown: https://developer.inoviopay.com/use-cases.md
Inovio covers the entire payment transaction process for any business, in any industry, from any bank source, with any endpoint. Find what you need below; each card links to the documentation and sample code for it.
## Explore use cases
## Ready to build?
Know which of the above apply? [Get started](https://developer.inoviopay.com/get-started.md) lists what you need from Inovio and how test and production work. To talk to someone first, [connect with an expert](https://www.inoviopay.com/contact).
---
# Solutions
The products on the Inovio platform, and which of them this site documents.
Source: https://developer.inoviopay.com/solutions.html
Markdown: https://developer.inoviopay.com/solutions.md
With flexible APIs and compatibility with any programming language, the Inovio platform makes it easy to set up and customize your desired payment experience. This page lists the products; the rest of the site documents the gateway, the SDKs and the cart plugins. For the others, contact your Inovio representative.
## Featured solutions
Global eCommerce gateway
Flexible, intelligent and 3D Secure. Multi-currency processing with intelligent routing across credit cards, US ACH, direct debit, SMS and electronic cash methods.
First-party plugins for WooCommerce, Magento 2, PrestaShop 9 and OpenCart 4 with tokenized checkout, 3D Secure, saved cards and settlement-aware refunds.
Online product catalogs with linked pricing and monthly subscription rebills, managed in the Inovio portal. Transactions reference catalog products by gateway product id.
## Third-party integrations
Inovio integrates with chargeback and fraud partners including Verifi (Order Insight), Ethoca, and CRM platforms through the postback service. See [Order Insight](https://developer.inoviopay.com/api/order-insight.md) and [Postback service](https://developer.inoviopay.com/api/postback.md).
---
# Using these docs with AI
How this site is built to be read by AI assistants and coding agents, and how to point one at it.
Source: https://developer.inoviopay.com/ai.html
Markdown: https://developer.inoviopay.com/ai.md
## Every page is Markdown
Every HTML page on this site has a Markdown twin at the same path with a `.md` extension. The Markdown is the source; the HTML is generated from it. Both carry the same headings, tables, code samples and anchors.
| You want | Fetch |
|---|---|
| One page | `https://developer.inoviopay.com/api/sale.md` |
| The site index with one-line summaries | `https://developer.inoviopay.com/llms.txt` |
| Every page in a single file | `https://developer.inoviopay.com/llms-full.txt` |
| The API as a machine-readable spec | `https://developer.inoviopay.com/openapi.yaml` |
| Every example request, importable | `https://developer.inoviopay.com/postman.json` |
Clients that send `Accept: text/markdown` get the Markdown for an `.html` URL without changing the path. Each HTML page also declares its twin with ``, carries `TechArticle` JSON-LD, and has a **Copy for AI** button in the header that copies the page as Markdown to your clipboard.
**curl**
```bash
curl -s -H "Accept: text/markdown" https://developer.inoviopay.com/api/sale.html
```
**Python**
```python
import urllib.request
md = urllib.request.urlopen("https://developer.inoviopay.com/api/sale.md").read().decode()
```
## Point an assistant at the site
**Claude Code, Cursor, Copilot, Windsurf and similar.** Paste the `llms.txt` URL into your project rules or a `docs` reference, or add this line to your project's `CLAUDE.md` / `.cursorrules`:
```markdown
Inovio gateway docs: https://developer.inoviopay.com/llms.txt (fetch the .md pages it lists before answering questions about the Inovio API or SDKs).
```
**ChatGPT, Claude.ai, Gemini.** Paste `https://developer.inoviopay.com/llms-full.txt` and ask your question; the full site is small enough to fit in a single context window.
**Code generation from the spec.** `openapi.yaml` is OpenAPI 3.1. The API is not REST: it is one form-encoded `POST` whose `request_action` field selects the operation, so use the spec for parameter names, enums and response fields rather than for generating a client. The SDKs are the supported clients.
## What agents should know about this API
These are the facts an assistant most often gets wrong when reasoning from generic payment-gateway knowledge. Each links to the page that proves it.
- **There is no sandbox host.** Test and production use the same URL and credentials; the portal configuration decides which merchant ID is hit. [Get started](https://developer.inoviopay.com/get-started.md)
- **A decline is a normal response, not an error.** `TRANS_STATUS_NAME=DECLINED` with `SERVICE_RESPONSE` and `PROCESSOR_RESPONSE` explaining why. The SDKs return it; they do not throw. [How the SDKs think](https://developer.inoviopay.com/sdks/concepts.md)
- **The tokenization HMAC does not include the card number.** The PDF says it does; the gateway disagrees. Sign `timestamp || unique_id || site_id` with the Site Key. [Tokenization](https://developer.inoviopay.com/api/tokenization.md)
- **A token replaces the PAN only.** You still send `pmt_expiry` and, where required, `pmt_key`. [Tokenization](https://developer.inoviopay.com/api/tokenization.md)
- **Send `CREDIT_ON_FAIL=1` on a reversal** and the gateway itself re-routes to a credit if the order has already settled. Never write a client-side "try reverse, then credit" fallback. [Reversal](https://developer.inoviopay.com/api/reversal.md)
- **`XTL_ORDER_ID` gives you idempotent retries.** A retried request with the same id returns the original result instead of charging twice. After a timeout, call `CCSTATUS` before retrying. [Order status](https://developer.inoviopay.com/api/status.md)
- **Amounts are decimal strings**, never floats. The SDKs reject `1.25` as a number and accept `"1.25"`. [How the SDKs think](https://developer.inoviopay.com/sdks/concepts.md)
- **The statement descriptor must not contain a space, underscore or slash.** The whole transaction is rejected otherwise. [Shopping cart plugins](https://developer.inoviopay.com/carts/index.md)
## Structure that makes the site easy to parse
- Stable, slugified section ids (`/api/sale.html#request-parameters`), unchanged from the previous site where they existed.
- One `h1` per page, `h2` per section, `h3` inside a section. No deeper nesting.
- Parameter tables always have the parameter in the first column with a `Required` or `Optional` badge (`(required)` / `(optional)` in the Markdown).
- Code samples are fenced with a language tag; example requests are complete and use the placeholder credentials `api_user` / `P@ssw0rd!` / site `12345`.
- `robots.txt` explicitly allows AI crawlers; `sitemap.xml` lists every page.
---
# API Overview
Introduction to the Inovio Payment Service API, its transport and encoding requirements, and how testing and production environments are separated.
Source: https://developer.inoviopay.com/api/overview.html
Markdown: https://developer.inoviopay.com/api/overview.md
The Inovio Payment Service API is designed to allow merchants to communicate and process online transactions with the payment gateway's transaction processing system. All cardholder and transaction data are sent over the internet using the TLS 1.2 cryptographic protocol.
The API expects all data to be URL and UTF-8 encoded, using character set ISO-8859-1 over HTTPS.
**Testing & Production Environments:** At Inovio, merchants use the exact same account credentials and API endpoints for both testing and production. The distinction lies purely in how your site is configured within the Inovio portal. During testing, your site's transactions will be pointed to a "Test Bank" Merchant ID (MID), allowing you to safely simulate payments. When you are ready to process real transactions, this configuration is simply updated in the portal to point to your live production MID(s), requiring zero changes to your code.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
Authentication parameters (`REQ_USERNAME`, `REQ_PASSWORD`, `SITE_ID`) are required on every request. You can verify your credentials are working with the `TESTAUTH` action, documented on the [Authentication](https://developer.inoviopay.com/api/authentication.md#auth) page.
## Service Availability
Use the `TESTGW` action to check if the Payment Service is available to process requests. This is useful for uptime monitoring and health checks.
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `TESTGW`. |
| `REQ_USERNAME` (required) | Merchant's Service username. |
| `REQ_PASSWORD` (required) | Merchant's Service password. |
| `SITE_ID` (required) | Merchant's website ID. |
| `REQUEST_API_VERSION` (required) | Must be sent as `4.14`. |
| `REQUEST_RESPONSE_FORMAT` (optional) | Format of the response. Accepts `XML` or `JSON`. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=TESTGW&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$health = $client->testAvailability();
echo $health->ok, "\n";
echo $health->outcome->service->code, ' ', $health->outcome->service->advice, "\n";
```
**Node**
```ts
import { InovioClient } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const health = await client.testAvailability();
console.log(health.ok);
console.log(health.outcome.service.code, health.outcome.service.advice);
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
health = client.test_availability()
print(health.ok)
print(health.outcome.service.code, health.outcome.service.advice)
```
**Java**
```java
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
HealthResult health = client.testAvailability();
System.out.println(health.ok());
System.out.println(health.outcome().service().code() + " " + health.outcome().service().advice());
```
**Response**
```json
{
"REQUEST_ACTION": "TESTGW",
"TRANS_STATUS_NAME": "",
"TRANS_VALUE": "",
"TRANS_ID": "",
"CUST_ID": "",
"XTL_CUST_ID": "",
"MERCH_ACCT_ID": "",
"CARD_BRAND_NAME": "",
"PMT_L4": "",
"API_RESPONSE": "0",
"API_ADVICE": " ",
"SERVICE_RESPONSE": 101,
"SERVICE_ADVICE": "Service Available",
"PROCESSOR_RESPONSE": 0,
"PROCESSOR_ADVICE": " ",
"INDUSTRY_RESPONSE": 0,
"INDUSTRY_ADVICE": " ",
"REF_FIELD": "",
"PROC_NAME": "",
"AVS_RESPONSE": "",
"CVV_RESPONSE": "",
"REQUEST_API_VERSION": "4.14",
"TRANS_NTOKEN_USED": 0
}
```
---
# Authentication
Required credential parameters sent with every gateway request, and how to verify them with the TESTAUTH action.
Source: https://developer.inoviopay.com/api/authentication.html
Markdown: https://developer.inoviopay.com/api/authentication.md
Authentication parameters are required on every request to the gateway. You can use the `TESTAUTH` action to verify your credentials are functioning correctly.
| Parameter | Description |
|---|---|
| `REQ_USERNAME` (required) | Merchant's Service username. |
| `REQ_PASSWORD` (required) | Merchant's Service password. |
| `SITE_ID` (required) | Merchant's website ID. |
| `REQUEST_API_VERSION` (required) | Must be sent as `4.14` on all requests. |
| `REQUEST_RESPONSE_FORMAT` (optional) | Format of the response. Accepts `XML` (default), `JSON`, or `PIPES`. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=TESTAUTH&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Errors\AuthenticationException;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
try {
$health = $client->testAuth();
echo $health->ok, "\n";
echo $health->outcome->service->code, ' ', $health->outcome->service->advice, "\n";
} catch (AuthenticationException $e) {
// Bad credentials raise an exception (API tier 101), never a decline.
echo 'rejected: ', $e->getMessage(), "\n";
}
```
**Node**
```ts
import { InovioClient, AuthenticationError } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
try {
const health = await client.testAuth();
console.log(health.ok);
console.log(health.outcome.service.code, health.outcome.service.advice);
} catch (e) {
// Bad credentials raise an error (API tier 101), never a decline.
if (e instanceof AuthenticationError) console.log('rejected:', e.message);
else throw e;
}
```
**Python**
```python
from inovio_gateway import AuthenticationError, Credentials, InovioClient
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
try:
health = client.test_auth()
print(health.ok)
print(health.outcome.service.code, health.outcome.service.advice)
except AuthenticationError as e:
# Bad credentials raise an exception (API tier 101), never a decline.
print("rejected:", e.message)
```
**Java**
```java
import com.inoviopay.gateway.errors.AuthenticationException;
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
try {
HealthResult health = client.testAuth();
System.out.println(health.ok());
System.out.println(health.outcome().service().code() + " " + health.outcome().service().advice());
} catch (AuthenticationException e) {
// Bad credentials raise an exception (API tier 101), never a decline.
System.out.println("rejected: " + e.getMessage());
}
```
**Response**
```json
{
"REQUEST_ACTION": "TESTAUTH",
"TRANS_STATUS_NAME": "",
"TRANS_VALUE": "",
"TRANS_ID": "",
"CUST_ID": "",
"XTL_CUST_ID": "",
"MERCH_ACCT_ID": "",
"CARD_BRAND_NAME": "",
"PMT_L4": "",
"API_RESPONSE": "0",
"API_ADVICE": " ",
"SERVICE_RESPONSE": 100,
"SERVICE_ADVICE": "User Authorized",
"PROCESSOR_RESPONSE": 0,
"PROCESSOR_ADVICE": " ",
"INDUSTRY_RESPONSE": 0,
"INDUSTRY_ADVICE": " ",
"REF_FIELD": "",
"PROC_NAME": "",
"AVS_RESPONSE": "",
"CVV_RESPONSE": "",
"REQUEST_API_VERSION": "4.14",
"TRANS_NTOKEN_USED": 0
}
```
---
# Tokenization Service
Exchange a card number for a one-time-use TOKEN_GUID, with verified HMAC signing and follow-up sale request requirements.
Source: https://developer.inoviopay.com/api/tokenization.html
Markdown: https://developer.inoviopay.com/api/tokenization.md
Rather than submitting the credit card number (`PMT_ID`) with a transaction, a token ID may be used instead. This one time use, unique ID is acquired by sending a request to the token service endpoint.
**POST** `https://api.inoviopay.com/payment/token_service.cfm`
| Parameter | Description |
|---|---|
| `card_pan` (required) | Customer card number to be tokenized. |
| `request_api_version` (required) | API Version (e.g., `4.14`). |
| `site_id` (required) | Merchant's website ID. |
| `unique_id` (required) | Alphanumeric ID linked to the request (Max 32 chars). |
| `x-timestamp` (required) | Format: `YYYYMMDDHHMMSS` (Uses UTC). Open for 5 minutes. |
| `x-signature` (required) | HMAC_SHA256 Base16 signature generated using x-timestamp and unique_id with the secret key. |
> **Verified: the HMAC signature excludes the card number**
> The v4.14 PDF, section 4.8.1.1, documents `x-signature` as generated from `x-timestamp`, `unique_id`, `card_pan`, and `site_id`. We verified against the gateway directly, and the gateway actually validates `hmac_sha256(timestamp || unique_id || site_id, site_key)`, with the PAN excluded from the signed message. Signing with the PAN included fails with error 121.
>
> The PDF and the verified gateway behavior disagree here. This page documents the verified, working behavior: sign `x-timestamp`, `unique_id`, and `site_id` only, and omit `card_pan` from the signed string.
> **The site key is not your API password**
> The site key used to compute the HMAC is a separate per-site HMAC secret issued by Inovio support. It is not the same as your `req_password` API password used on `pmt_service.cfm` requests. Contact your gateway support representative to obtain it.
> **TOKEN_GUID still requires pmt_expiry**
> A `TOKEN_GUID` replaces `PMT_NUMB` only, in a subsequent sale or auth request. You must still send `pmt_expiry` (and `pmt_key`, when the processor requires it) alongside `TOKEN_GUID`. Omitting `pmt_expiry` causes the API to respond with error 110, "Required field".
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/token_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "card_pan=4111111111111111&request_api_version=4.14&site_id=12345&unique_id=REQ999&x-timestamp=20241209230900&x-signature=8fa1f..."
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
// The site key is a separate constructor argument, not your req_password.
$client = new InovioClient(
new Credentials('api_user', 'P@ssw0rd!', '12345'),
'SANDBOX',
siteKey: 'your-hmac-site-key'
);
$token = $client->tokenize(PaymentMethods::card('4111111111111111', '122026', '123'));
echo $token->token->guid(), "\n";
// The token replaces the PAN only — expiry travels with it automatically.
$sale = $client->sale((new TransactionRequest(
$token->token,
[new LineItem('SKU-1', 1, Money::of('10.00', 'USD'))]
))->withIdempotency('TOK-ORDER-1'));
echo $sale->status, "\n";
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
// The site key is passed via client options, not req_password.
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX', siteKey: 'your-hmac-site-key' }
);
const token = await client.tokenize(PaymentMethods.card('4111111111111111', '122026', '123'));
console.log(token.token.guid);
// The token replaces the PAN only — expiry travels with it automatically.
const sale = await client.sale({
paymentMethod: token.token,
lineItems: [{ productId: 'SKU-1', count: 1, value: Money.of('10.00', 'USD') }],
idempotency: { xtlOrderId: 'TOK-ORDER-1' },
});
console.log(sale.status);
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods
from inovio_gateway.model import Idempotency, LineItem
from inovio_gateway.request import TransactionRequest
# The site key is a client constructor argument, not the API password.
client = InovioClient(
Credentials("api_user", "P@ssw0rd!", "12345"),
environment="SANDBOX",
site_key="your-hmac-site-key",
)
token = client.tokenize(PaymentMethods.card("4111111111111111", "122026", "123"))
print(token.token.guid)
# The token replaces the PAN only — expiry travels with it automatically.
req = TransactionRequest(
payment_method=token.token,
line_items=[LineItem("SKU-1", 1, Money.of("10.00", "USD"))],
idempotency=Idempotency(xtl_order_id="TOK-ORDER-1"),
)
sale = client.sale(req)
print(sale.status.value)
```
**Java**
```java
// The site key is set on InovioClient.Options, not the req_password field.
InovioClient.Options options = new InovioClient.Options();
options.siteKey = "your-hmac-site-key";
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"), options);
Tokenize.Result token = client.tokenize(
PaymentMethods.card("4111111111111111", "122026", "123"));
System.out.println(token.token().guid());
// The token replaces the PAN only — expiry travels with it automatically.
TransactionRequest req = new TransactionRequest(
token.token(), new LineItem("SKU-1", 1, Money.of("10.00", "USD")))
.idempotency("TOK-ORDER-1");
TransactionResult sale = client.sale(req);
System.out.println(sale.status());
```
**Response**
```json
{
"TOKEN_GUID": "3A393EBC3A266B9842FC55DB44B5401C697A5768",
"TOKEN_IP": "192.168.1.1",
"TOKEN_REQID": "73393758223"
}
```
---
# Easy PCI Compliance
Inovio's solutions, including hosted checkout and payment pages, are PCI compliant, providing unsurpassed security for customer account data. We make PCI compliance easy.
Source: https://developer.inoviopay.com/api/pci.html
Markdown: https://developer.inoviopay.com/api/pci.md
*I want to avoid having a PCI compliance burden.*
Inovio's Hosted Checkout page and Hosted Payment Form make it easy for merchants to accept payments online without actually interacting with customer payment data themselves. Since we are PCI compliant, merchants with our Hosted Checkout page are PCI compliant, providing unsurpassed security for customer account data.
## Sample code
[View a sample hosted payment page](https://svc.arguspayments.com/paymentpage/load/10191/16854/34538)
## Ready to get started?
[Get started](https://developer.inoviopay.com/get-started.md) or [connect with an expert](https://developer.inoviopay.com/contact.md).
---
# Authorize (CCAUTHORIZE)
Place a temporary hold on a cardholder's account to confirm funds are available, without capturing them.
Source: https://developer.inoviopay.com/api/authorize.html
Markdown: https://developer.inoviopay.com/api/authorize.md
An authorization request is used to confirm the availability of funds in the cardholder's bank account. This type of transaction places a temporary hold or pending "auth" on the cardholder's account and does not guarantee payment. For this type of transaction, merchants must send the service request action `CCAUTHORIZE`.
An authorization is the first part of a two-stage process of "authorizing" and "capturing" funds. This two-stage process is commonly used by merchants who fulfill partial orders or physical goods that have not yet shipped. In order to capture the Authorization request, use the `CCCAPTURE` action.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
> **WAIT TIME REQUIREMENT**
> Please allow a wait time of **120 seconds** for `CCAUTHORIZE`, `CCAUTHCAP`, and other requests. Even though the gateway usually processes requests in less than a second, downstream processors can take much longer to get a response from issuing banks.
> **EARLY TIMEOUT WARNING**
> If a merchant chooses to time out their request earlier than 120 seconds:
> - The gateway is not responsible for lost transactions, transaction records, or other failed functions.
> - The merchant may use `CCSTATUS` at a later time to find the final outcome of the request (provided an `XTL_ORDER_ID` was included in the original request). See [Order Status](https://developer.inoviopay.com/api/status.md#status).
> - Webhooks/postbacks may or may not trigger when the request is finalized at the gateway and should not be strictly relied upon.
### Request Parameters
The table below includes all required authentication, payment, and optional fields permitted during an authorization.
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCAUTHORIZE`. |
| `REQ_USERNAME` (required) | Service Request Username. |
| `REQ_PASSWORD` (required) | Service Request Password. |
| `REQUEST_RESPONSE_FORMAT` (optional) | Accepted values: "XML", "PIPES" and "JSON" (default: XML). |
| `REQUEST_API_VERSION` (required) | API Version (Must be 4.14). |
| `SITE_ID` (required) | Merchant's Website ID. |
| `CUST_FNAME` (optional) | Cardholder's First Name. |
| `CUST_LNAME` (optional) | Cardholder's Last Name. |
| `CUST_EMAIL` (optional) | Cardholder's Email Address. |
| `LI_COUNT_1` (required) | Line Item Count (Max value is "99"). |
| `LI_PROD_ID_1` (required) | Line Item Product ID 1. |
| `LI_VALUE_1` (required) | Line Item Transaction Amount 1. |
| `XTL_ORDER_ID` (optional) | Merchant's Order ID. |
| `BILL_ADDR` (optional) | Cardholder Billing Street Address (may be required by the bank for AVS). |
| `BILL_ADDR_CITY` (optional) | Cardholder's Billing City. |
| `BILL_ADDR_STATE` (optional) | Cardholder's Billing State (2-letter State or Territory Code). |
| `BILL_ADDR_ZIP` (optional) | Cardholder's Billing Postal/ZIP code. |
| `BILL_ADDR_COUNTRY` (optional) | Cardholder's Billing Country (2-letter Country Code ISO 3166-1 alpha-2). |
| `PMT_NUMB` (required) | Credit Card Number. |
| `TOKEN_GUID` (optional) | Token ID used in place of pmt_numb. See [Tokenization](https://developer.inoviopay.com/api/tokenization.md#tokenization). |
| `PMT_KEY` (required) | Credit Card CVV2 or CVC2 Code. |
| `PMT_EXPIRY` (required) | Credit Card Expiration Date (Format: `MMYYYY`, e.g. "122026"). |
| `CUST_LOGIN` (optional) | Cardholder's Login or User Name. |
| `CUST_PASSWORD` (optional) | Cardholder's Password. |
| `MERCH_ACCT_ID` (optional) | Merchant Account ID. If null, the system will follow merchant's bank settings. |
| `REQUEST_CURRENCY` (required) | 3-letter Currency Code (e.g., USD). |
| `PMT_DESCRIPTOR` (optional) | Dynamic Descriptor. This parameters will replace the static statement descriptor that appears in the cardholder's bank statement. (Not supported by all Processors) |
| `PMT_DESCRIPTOR_PHONE` (optional) | Bank Dynamic Customer Support Phone Number. This parameters will replace the static statement descriptor that appears in the cardholder's bank statement. (Not supported by all Processors) |
| `PMT_DESCRIPTOR_CITY` (optional) | Bank Dynamic Customer Support City (Applicable to MasterCard only). |
| `CUST_PHONE` (optional) | Cardholder's Phone Number. |
| `REQUEST_AFF_ID` (optional) | External Affiliate ID. |
| `REQUEST_AFF_ID_SUB` (optional) | External Sub-affiliate ID. |
| `UNIQUE_XTL_ORDER_ID` (optional) | Enforces External Order ID uniqueness ("0" - disables, "1" - declines, "2" - returns Approval). |
| `PMT_ID_XTL` (optional) | External Payment Unique Identifier. |
| `MBSHP_ID_XTL` (optional) | External Membership ID. |
| `TRANS_REBILL_TYPE` (optional) | Rebill Type (NONE, TRIAL, INITIAL, REBILL). |
| `REQUEST_INITIATOR` (optional) | CIT (C) and MIT (M) Used to identify transaction origination. |
| `REQUEST_INSTALLMENT` (optional) | 0 (not used), 1 (used) Used to denote if the rebill is an installment. |
| `PMT_NUMB_COF` (optional) | 0 (not used), 1 (used) Used to denote use of payment number stored by Merchant. |
| `REQUEST_REBILL` (optional) | 1 (yes for rebill), 2 (Start Subscription), not used. |
| `TRANS_TRIAL_REBILL_CUSTOMER_CONSENT` (optional) | Customer Consent for rebill after trial period. Required for first REBILL following TRIAL. |
| `TRANS_CUSTOMER_RECEIPT` (optional) | Payment Receipt content sent to the customer. |
| `CARD_ON_FILE_FLAG` (optional) | Flags if COF or New PMT Card (0=COF, 1=New). |
### Handling the Response
While the gateway returns a comprehensive payload for data modeling, you should primarily monitor the following fields to determine the outcome of an authorization, especially in cases of partial or variable authorizations:
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the transaction was `APPROVED`. |
| `TRANS_VALUE` | The exact amount the issuing bank authorized. If this is less than your requested amount, it was a partial authorization. |
| `PO_ID` | The Purchase Order ID. You must pass this exact value into your subsequent `CCCAPTURE` request to settle the funds. |
| `TRANS_ID` | The unique Transaction ID for the authorization. |
| `PROC_AUTH_RESPONSE` | The authorization code generated by the processor. |
| `AVS_RESPONSE / CVV_RESPONSE` | Indicates if the address and security codes matched, allowing you to apply internal risk modeling. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHORIZE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=4111111111111111&pmt_expiry=122026&pmt_key=123&request_currency=USD&li_value_1=84.50&cust_fname=John&cust_lname=Doe&bill_addr=123+Main+St&bill_addr_city=Los+Angeles&bill_addr_state=CA&bill_addr_zip=90001&bill_addr_country=US"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{Address, Customer, LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[new LineItem('SKU-492', 1, Money::of('84.50', 'USD'))]
);
$req->customer = new Customer();
$req->customer->firstName = 'John';
$req->customer->lastName = 'Doe';
$req->billingAddress = new Address();
$req->billingAddress->line1 = '123 Main St';
$req->billingAddress->city = 'Los Angeles';
$req->billingAddress->state = 'CA';
$req->billingAddress->zip = '90001';
$req->billingAddress->country = 'US';
$auth = $client->authorize($req);
echo $auth->status, ' order=', $auth->orderRef?->poId() ?? '-', "\n";
// Keep $auth->orderRef — capture() and reverse() both consume it.
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const auth = await client.authorize({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [{ productId: 'SKU-492', count: 1, value: Money.of('84.50', 'USD') }],
customer: { firstName: 'John', lastName: 'Doe' },
billingAddress: { line1: '123 Main St', city: 'Los Angeles', state: 'CA', zip: '90001', country: 'US' },
});
console.log(auth.status, 'order=', auth.orderRef?.poId ?? '-');
// Keep auth.orderRef — capture() and reverse() both consume it.
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods
from inovio_gateway.model import Address, Customer, LineItem
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
req = TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[LineItem("SKU-492", 1, Money.of("84.50", "USD"))],
)
req.customer = Customer(first_name="John", last_name="Doe")
req.billing_address = Address(
line1="123 Main St", city="Los Angeles", state="CA", zip="90001", country="US"
)
auth = client.authorize(req)
print(auth.status.value, "order=", auth.order_ref.po_id if auth.order_ref else "-")
# Keep auth.order_ref — capture() and reverse() both consume it.
```
**Java**
```java
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("SKU-492", 1, Money.of("84.50", "USD")));
Customer customer = new Customer();
customer.firstName = "John";
customer.lastName = "Doe";
req.customer = customer;
Address billing = new Address();
billing.line1 = "123 Main St";
billing.city = "Los Angeles";
billing.state = "CA";
billing.zip = "90001";
billing.country = "US";
req.billingAddress = billing;
TransactionResult auth = client.authorize(req);
System.out.println(auth.status() + " order=" + (auth.orderRef() == null ? "-" : auth.orderRef().poId()));
// Keep auth.orderRef() — capture() and reverse() both consume it.
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHORIZE",
"REQ_ID": "83920174",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 84.50,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 84.50,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 9482710345,
"CUST_ID": 1029384,
"XTL_CUST_ID": "xTr92mPqL1",
"PO_ID": 55829102,
"XTL_ORDER_ID": "",
"BATCH_ID": 883921,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 449201,
"CARD_BRAND_NAME": "Mastercard",
"CARD_TYPE": "MASTERCARD WORLD",
"CARD_CLASS": "Consumer Credit",
"CARD_PREPAID": 0,
"CARD_BANK": "CAPITAL ONE",
"CARD_COUNTRY": "USA",
"CARD_DETAIL": "Credit",
"CARD_BALANCE": "",
"PMT_L4": "5521",
"PMT_ID": 8839201,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "APV992",
"PROC_RETRIEVAL_NUM": "9A8B7C6D-5E4F-3A2B-1C0D-E9F8A7B6C5D4",
"PROC_REFERENCE_NUM": "REF88291039",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "Y",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928172",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "84.50",
"PO_LI_PROD_ID_1": "SKU-492",
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
---
# Sale (CCAUTHCAP)
Authorize and capture funds in a single request.
Source: https://developer.inoviopay.com/api/sale.html
Markdown: https://developer.inoviopay.com/api/sale.md
The Authorization and Capture request will authorize and request capture of the funds in a single request to the Payment Service.
See [Authorize](https://developer.inoviopay.com/api/authorize.md#authorize) for the full parameter table; the fields required for `CCAUTHCAP` are identical to those required for `CCAUTHORIZE`.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
### Request Parameters
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCAUTHCAP`. |
| `XTL_ORDER_ID` (optional) | Merchant's Order ID to associate with the transaction. |
### Handling the Response
Monitor these key fields to verify the sale was successfully authorized and captured. If a membership was created during this transaction, you will also receive the new membership ID.
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the transaction was `APPROVED`. |
| `TRANS_VALUE_SETTLED` | The final amount that has been captured and settled. |
| `TRANS_ID / PO_ID` | Unique identifiers for the transaction and the overarching order. |
| `PROC_AUTH_RESPONSE` | The authorization code generated by the processor. |
| `MBSHP_ID_x` | If the sale triggered a membership creation, the newly generated Membership ID will be returned here. |
| `TRANS_NTOKEN_USED` | Indicates if a Network Token (1) or raw PAN (0) was used for the transaction. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHCAP&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=4111111111111111&pmt_expiry=122026&pmt_key=123&request_currency=USD&li_value_1=49.99&xtl_order_id=INV-999"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = (new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[new LineItem('SKU-992', 1, Money::of('49.99', 'USD'))]
))->withIdempotency('INV-999'); // retry-safe by default
$sale = $client->sale($req);
switch ($sale->status) {
case 'APPROVED':
echo 'fulfil order ', $sale->orderRef?->poId(), "\n";
break;
case 'DECLINED':
echo 'declined: ', $sale->outcome->service->code, "\n";
break;
case 'PENDING':
echo 'pending: ', $sale->nextAction->kind ?? '?', "\n";
break;
}
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const sale = await client.sale({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [{ productId: 'SKU-992', count: 1, value: Money.of('49.99', 'USD') }],
idempotency: { xtlOrderId: 'INV-999' }, // retry-safe by default
});
switch (sale.status) {
case 'APPROVED':
console.log('fulfil order', sale.orderRef?.poId);
break;
case 'DECLINED':
console.log('declined:', sale.outcome.service.code);
break;
case 'PENDING':
console.log('pending:', sale.nextAction?.kind);
break;
}
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods, TransactionStatus
from inovio_gateway.model import Idempotency, LineItem
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
req = TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[LineItem("SKU-992", 1, Money.of("49.99", "USD"))],
idempotency=Idempotency(xtl_order_id="INV-999"), # retry-safe by default
)
sale = client.sale(req)
if sale.status is TransactionStatus.APPROVED:
print("fulfil order", sale.order_ref.po_id if sale.order_ref else "-")
elif sale.status is TransactionStatus.DECLINED:
print("declined:", sale.outcome.service.code)
elif sale.status is TransactionStatus.PENDING:
print("pending:", sale.next_action.kind if sale.next_action else "?")
```
**Java**
```java
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("SKU-992", 1, Money.of("49.99", "USD")))
.idempotency("INV-999"); // retry-safe by default
TransactionResult sale = client.sale(req);
switch (sale.status()) {
case APPROVED:
System.out.println("fulfil order " + sale.orderRef().poId());
break;
case DECLINED:
System.out.println("declined: " + sale.outcome().service().code());
break;
case PENDING:
System.out.println("pending: " + sale.nextAction().kind());
break;
default:
break;
}
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHCAP",
"REQ_ID": "68192033",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 49.99,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 49.99,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 8839201112,
"CUST_ID": 9928102,
"XTL_CUST_ID": "cUsT992xP",
"PO_ID": 77281920,
"XTL_ORDER_ID": "INV-999",
"BATCH_ID": 882910,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Mastercard",
"CARD_TYPE": "MASTERCARD BLACK CARD",
"CARD_CLASS": "Consumer Credit",
"CARD_PREPAID": 0,
"CARD_BANK": "CREDOMATIC INTERNATIONAL",
"CARD_COUNTRY": "CRI",
"CARD_DETAIL": "Credit",
"CARD_BALANCE": "",
"PMT_L4": "3535",
"PMT_ID": 8829102,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "AUTH99",
"PROC_RETRIEVAL_NUM": "1029384A-B8C7-D6E5-F4G3-H2I1J0K9L8M7",
"PROC_REFERENCE_NUM": "REF10293847",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "8829103",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "49.99",
"PO_LI_PROD_ID_1": "SKU-992",
"MBSHP_ID_1": "88291",
"TRANS_NTOKEN_USED": 1
}
```
---
# Delayed Capture (CCCAPTURE)
Capture funds against a previously placed authorization, in full or in partial amounts.
Source: https://developer.inoviopay.com/api/capture.html
Markdown: https://developer.inoviopay.com/api/capture.md
In order to capture a pending authorization, merchants must send a capture request. The Payment Service allows multiple partial captures as long as the original authorized amount is not exceeded.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
### Request Parameters
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCCAPTURE`. |
| `REQUEST_REF_PO_ID` (required) | Reference Order ID (`PO_ID`) of the original authorization. |
| `LI_VALUE_1` (required) | Amount to capture. This value can be less than or equal to the originally authorized amount. |
### Handling the Response
Monitor these key fields to verify the funds from your original authorization have been successfully captured and queued for settlement.
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the capture was `APPROVED`. |
| `TRANS_VALUE_SETTLED` | The exact amount that was successfully captured and will be settled to your account. |
| `TRANS_ID` | The unique Transaction ID for this specific capture event (this will be different from the original authorization's TRANS_ID). |
| `PO_ID` | The overarching Purchase Order ID tying this capture to the original authorization. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCCAPTURE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id=18103630&li_value_1=49.99"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\Money;
use Inovio\Gateway\Refs\Refs;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$cap = $client->capture(Refs::order('18103630'), Money::of('49.99', 'USD'));
echo $cap->status, "\n";
echo 'settled=', var_export($cap->settled, true), "\n"; // batch flips this later — not a failure
// Omit the amount to capture the full authorized amount instead:
// $client->capture(Refs::order('18103630'));
```
**Node**
```ts
import { InovioClient, Money, Refs } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const cap = await client.capture(Refs.order('18103630'), Money.of('49.99', 'USD'));
console.log(cap.status);
console.log('settled=', cap.settled); // batch flips this later — not a failure
// Omit the amount to capture the full authorized amount instead:
// await client.capture(Refs.order('18103630'));
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, Refs
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
cap = client.capture(Refs.order("18103630"), Money.of("49.99", "USD"))
print(cap.status.value)
print("settled=", cap.settled) # batch flips this later — not a failure
# Omit the amount to capture the full authorized amount instead:
# client.capture(Refs.order("18103630"))
```
**Java**
```java
import com.inoviopay.gateway.refs.Refs;
TransactionResult cap = client.capture(Refs.order("18103630"), Money.of("49.99", "USD"));
System.out.println(cap.status());
System.out.println("settled=" + cap.settled()); // batch flips this later — not a failure
// Omit the amount to capture the full authorized amount instead:
// client.capture(Refs.order("18103630"));
```
**Response**
```json
{
"REQUEST_ACTION": "CCCAPTURE",
"REQ_ID": "74839201",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 49.99,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 49.99,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572109,
"CUST_ID": 7382910,
"XTL_CUST_ID": "xT883mP",
"PO_ID": 18103630,
"XTL_ORDER_ID": "INV-999",
"BATCH_ID": 883921,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Visa",
"CARD_TYPE": "VISA CLASSIC",
"CARD_CLASS": "Consumer Debit",
"CARD_PREPAID": 1,
"CARD_BANK": "BOFI FEDERAL BANK",
"CARD_COUNTRY": "USA",
"CARD_DETAIL": "Debit",
"CARD_BALANCE": "",
"PMT_L4": "2921",
"PMT_ID": 4895565,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "AUTH881",
"PROC_RETRIEVAL_NUM": "7D8A9F0E-1B2C-3D4E-5F6A-7B8C9D0E1F2A",
"PROC_REFERENCE_NUM": "REF44920193",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928173",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": 49.99,
"PO_LI_PROD_ID_1": 111205,
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
---
# Credit (CCCREDIT)
Return funds for a captured or settled authorization.
Source: https://developer.inoviopay.com/api/credit.html
Markdown: https://developer.inoviopay.com/api/credit.md
Merchants may request to return funds for a captured authorization. Merchants may only credit transactions that have been captured or settled.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
### Request Parameters
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCCREDIT`. |
| `REQUEST_REF_PO_ID` (required) | Reference Order ID (`PO_ID`) of the original transaction. |
| `LI_VALUE_1` (required) | Amount to credit. The amount may not exceed the original authorization's total amount. |
### Handling the Response
When processing a refund, monitor the response to confirm the credit was applied to the original order successfully.
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the credit was `APPROVED`. |
| `TRANS_VALUE` | The approved credit amount. Note that refunds will return as a negative value (e.g., `-10.00`). |
| `TRANS_ID` | The unique Transaction ID assigned specifically to this refund event. |
| `PO_ID` | The original Purchase Order ID, linking this credit back to the initial transaction. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCCREDIT&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id=18103630&li_value_1=10.00"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\Money;
use Inovio\Gateway\Refs\Refs;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
// You can only refund what was captured.
$refund = $client->refund(Refs::order('18103630'), Money::of('10.00', 'USD'));
echo $refund->status, "\n";
echo $refund->amount?->toWire(), "\n"; // negative on the wire
// Omit the amount to refund the full order instead:
// $client->refund(Refs::order('18103630'));
```
**Node**
```ts
import { InovioClient, Money, Refs } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
// You can only refund what was captured.
const refund = await client.refund(Refs.order('18103630'), Money.of('10.00', 'USD'));
console.log(refund.status);
console.log(refund.amount?.amount); // negative on the wire
// Omit the amount to refund the full order instead:
// await client.refund(Refs.order('18103630'));
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, Refs
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
# You can only refund what was captured.
refund = client.refund(Refs.order("18103630"), Money.of("10.00", "USD"))
print(refund.status.value)
print(refund.amount.to_wire() if refund.amount else "-") # negative on the wire
# Omit the amount to refund the full order instead:
# client.refund(Refs.order("18103630"))
```
**Java**
```java
import com.inoviopay.gateway.refs.Refs;
// You can only refund what was captured.
TransactionResult refund = client.refund(Refs.order("18103630"), Money.of("10.00", "USD"));
System.out.println(refund.status());
System.out.println(refund.amount() == null ? "-" : refund.amount().toWire()); // negative on the wire
// Omit the amount to refund the full order instead:
// client.refund(Refs.order("18103630"));
```
**Response**
```json
{
"REQUEST_ACTION": "CCCREDIT",
"REQ_ID": "84729103",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": -10.00,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": -10.00,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572110,
"CUST_ID": 7382910,
"XTL_CUST_ID": "xT883mP",
"PO_ID": 18103630,
"XTL_ORDER_ID": "INV-999",
"BATCH_ID": 883922,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Visa",
"CARD_TYPE": "VISA CLASSIC",
"CARD_CLASS": "Consumer Debit",
"CARD_PREPAID": 1,
"CARD_BANK": "BOFI FEDERAL BANK",
"CARD_COUNTRY": "USA",
"CARD_DETAIL": "Debit",
"CARD_BALANCE": "",
"PMT_L4": "2921",
"PMT_ID": 4895565,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "REF81371",
"PROC_RETRIEVAL_NUM": "A1B2C3D4-E5F6-7A8B-9C0D-E1F2A3B4C5D6",
"PROC_REFERENCE_NUM": "REF755033258",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928174",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": -10.00,
"PO_LI_PROD_ID_1": "111205",
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
---
# Reversal (CCREVERSE)
Reverse or void an authorization before it settles, with an option to auto-reroute to a credit if it already has.
Source: https://developer.inoviopay.com/api/reversal.html
Markdown: https://developer.inoviopay.com/api/reversal.md
Merchants may request to reverse or void the original authorization before it is settled.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
### Request Parameters
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCREVERSE`. |
| `REQUEST_REF_PO_ID` (required) | Reference Order ID (`PO_ID`) of the original transaction. |
| `CREDIT_ON_FAIL` (optional) | If set to `1`, the system will automatically attempt to credit the transaction if the reversal request fails (e.g., if it already settled). |
### Handling the Response
Monitor the response to verify the authorization was successfully voided before settlement.
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the reversal was `APPROVED`. |
| `TRANS_VALUE` | The approved reversal amount. Similar to credits, this will return as a negative value (e.g., `-25.00`). |
| `TRANS_ID` | The unique Transaction ID assigned specifically to this reversal event. |
| `PO_ID` | The original Purchase Order ID linking this void back to the initial authorization. |
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCREVERSE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id=18103630&credit_on_fail=1"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Refs\Refs;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
// creditOnFail: true sets CREDIT_ON_FAIL=1 — if the order already settled,
// the gateway re-routes this call to CCCREDIT instead of declining.
$reversed = $client->reverse(Refs::order('18103630'), creditOnFail: true);
echo $reversed->status, "\n";
echo $reversed->amount?->toWire(), "\n"; // negative on the wire
```
**Node**
```ts
import { InovioClient, Refs } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
// NOTE: CREDIT_ON_FAIL is not exposed on reverse() yet — call the API
// directly (request_action=CCREVERSE&credit_on_fail=1) for the auto-credit reroute.
const reversed = await client.reverse(Refs.order('18103630'));
console.log(reversed.status);
console.log(reversed.amount?.amount); // negative on the wire
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Refs
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
# NOTE: CREDIT_ON_FAIL is not exposed on reverse() yet — call the API
# directly (request_action=CCREVERSE&credit_on_fail=1) for the auto-credit reroute.
reversed_tx = client.reverse(Refs.order("18103630"))
print(reversed_tx.status.value)
print(reversed_tx.amount.to_wire() if reversed_tx.amount else "-") # negative on the wire
```
**Java**
```java
import com.inoviopay.gateway.refs.Refs;
// NOTE: CREDIT_ON_FAIL is not exposed on reverse() yet — call the API
// directly (request_action=CCREVERSE&credit_on_fail=1) for the auto-credit reroute.
TransactionResult reversed = client.reverse(Refs.order("18103630"));
System.out.println(reversed.status());
System.out.println(reversed.amount() == null ? "-" : reversed.amount().toWire()); // negative on the wire
```
**Response**
```json
{
"REQUEST_ACTION": "CCREVERSE",
"REQ_ID": "93827164",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": -25.00,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": -25.00,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572111,
"CUST_ID": 7382910,
"XTL_CUST_ID": "xT883mP",
"PO_ID": 18103630,
"XTL_ORDER_ID": "",
"BATCH_ID": 883922,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Visa",
"CARD_TYPE": "VISA CLASSIC",
"CARD_CLASS": "Consumer Debit",
"CARD_PREPAID": 1,
"CARD_BANK": "BOFI FEDERAL BANK",
"CARD_COUNTRY": "USA",
"CARD_DETAIL": "Debit",
"CARD_BALANCE": "",
"PMT_L4": "2921",
"PMT_ID": 4895565,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "VOID813",
"PROC_RETRIEVAL_NUM": "B2C3D4E5-F6A7-8B9C-0D1E-2F3A4B5C6D7E",
"PROC_REFERENCE_NUM": "REF187604789",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928175",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": -25.00,
"PO_LI_PROD_ID_1": "111205",
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
## Reverse or credit in one call: CREDIT_ON_FAIL
`CCREVERSE` reverses the original authorization; `CCREVERSECAP` reverses a successful `CCCAPTURE` transaction instead. Both actions accept `CREDIT_ON_FAIL`.
Setting `CREDIT_ON_FAIL=1` on `CCREVERSE` (or `CCREVERSECAP`) tells the gateway that if the order is already settled, and therefore cannot be reversed, the gateway itself re-routes the request to `CCCREDIT` instead of just failing. You send one request and the gateway decides whether a reversal or a credit is the correct operation for the order's current state.
> **Verified against the gateway**
> This re-route behavior was verified against the gateway directly, not just claimed in the spec. The response for a re-routed request comes back with `REQUEST_ACTION` equal to `CCCREDIT`, not `CCREVERSE`. Check `REQUEST_ACTION` in the response, not just `TRANS_STATUS_NAME`, if you need to know server-side whether the re-route happened.
>
> Without `CREDIT_ON_FAIL`, attempting to reverse an already-settled order returns gateway service code 515 (a decline) instead of succeeding.
Because the re-route is transparent, do not write your own settlement pre-check before deciding whether to call `CCREVERSE` or `CCCREDIT`. Send `CCREVERSE` with `CREDIT_ON_FAIL=1` and read `REQUEST_ACTION` off the response to see which operation the gateway actually performed.
---
# Order Status (CCSTATUS)
Look up the full transaction history of an order by the gateway's order ID or your own external reference.
Source: https://developer.inoviopay.com/api/status.html
Markdown: https://developer.inoviopay.com/api/status.md
Check the status of an order. You can query using the Gateway's Order ID or your external reference ID.
**POST** `https://api.inoviopay.com/payment/pmt_service.cfm`
### Request Parameters
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `CCSTATUS`. |
| `REQUEST_REF_PO_ID` (optional) | Lookup using the Gateway's `PO_ID`. |
| `REQUEST_REF_PO_ID_XTL` (optional) | Lookup using the Merchant's `XTL_ORDER_ID`. |
### Handling the Response
Unlike standard transaction responses, `CCSTATUS` returns a matrix format to accommodate the entire lifecycle of an order. The response contains two arrays: a `COLUMNS` array defining the fields, and a `DATA` array containing one or more rows of transaction events (e.g., the original authorization, subsequent captures, refunds, or chargebacks).
| Array Name | Description |
|---|---|
| `COLUMNS` | An ordered list of string values representing the field names (e.g., `REQUEST_ACTION`, `TRANS_STATUS_NAME`, `TRANS_VALUE`). |
| `DATA` | An array of arrays. Each inner array represents a distinct transaction event tied to the order. To parse the data, map the index of a value in the inner array to the corresponding index in the `COLUMNS` array. |
For example, in the response below, `COLUMNS[0]` is `REQUEST_ACTION` and `COLUMNS[1]` is `TRANS_STATUS_NAME`, so `DATA[0][0]` (`"CCAUTHCAP"`) and `DATA[0][1]` (`"APPROVED"`) describe the same event: the first row's request action and its status. The response below has three rows: the original `CCAUTHCAP`, a subsequent `CCCREDIT`, and a `CCCHARGEBACK`, each identified the same way by walking `COLUMNS` alongside the row.
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCSTATUS&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id_xtl=INV-999"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Refs\Refs;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$s = $client->status(Refs::xtlOrder('INV-999'));
echo 'legs=', count($s->transactions), "\n";
echo 'authorized=', $s->authorized->toWire(), "\n";
echo 'captured=', $s->captured->toWire(), "\n";
echo 'refunded=', $s->refunded->toWire(), "\n";
echo 'net=', $s->net->toWire(), "\n"; // captured - refunded
echo 'outstanding=', $s->outstanding->toWire(), "\n"; // authorized - captured
```
**Node**
```ts
import { InovioClient, Refs } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const s = await client.status(Refs.xtlOrder('INV-999'));
console.log('legs=', s.transactions.length);
console.log('authorized=', s.authorized?.amount);
console.log('captured=', s.captured?.amount);
console.log('refunded=', s.refunded?.amount);
console.log('net=', s.net?.amount); // captured - refunded
console.log('outstanding=', s.outstanding?.amount); // authorized - captured
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Refs
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
s = client.status(Refs.xtl_order("INV-999"))
print("legs=", len(s.transactions))
print("authorized=", s.authorized.to_wire())
print("captured=", s.captured.to_wire())
print("refunded=", s.refunded.to_wire())
print("net=", s.net.to_wire()) # captured - refunded
print("outstanding=", s.outstanding.to_wire()) # authorized - captured
```
**Java**
```java
import com.inoviopay.gateway.refs.Refs;
import com.inoviopay.gateway.result.OrderStatus;
OrderStatus s = client.status(Refs.xtlOrder("INV-999"));
System.out.println("legs=" + s.transactions().size());
System.out.println("authorized=" + s.authorized().toWire());
System.out.println("captured=" + s.captured().toWire());
System.out.println("refunded=" + s.refunded().toWire());
System.out.println("net=" + s.net().toWire()); // captured - refunded
System.out.println("outstanding=" + s.outstanding().toWire()); // authorized - captured
```
**Response**
```json
{
"COLUMNS": [
"REQUEST_ACTION",
"TRANS_STATUS_NAME",
"TRANS_VALUE",
"TRANS_ID",
"CUST_ID",
"XTL_CUST_ID",
"PO_ID",
"XTL_ORDER_ID",
"BATCH_ID",
"PROC_NAME",
"MERCH_ACCT_ID",
"CARD_BRAND_NAME",
"PMT_ID",
"PMT_L4",
"PROC_UDF01",
"PROC_UDF02",
"PROC_AUTH_RESPONSE",
"PROC_RETRIEVAL_NUM",
"PROC_REFERENCE_NUM",
"AVS_RESPONSE",
"CVV_RESPONSE",
"CURR_CODE_ALPHA",
"CURR_NAME",
"TRANS_ID"
],
"DATA": [
[
"CCAUTHCAP",
"APPROVED",
125.50,
485729103,
9820193,
"xtLCust88",
8819203,
"INV-882",
88291,
"Inovio Primary",
110203,
"Visa",
3920192,
"1111",
"",
"",
"AUTH881",
"B03C2B04-00A5-4179-A9D94F0CDD76197D",
"REF847156508",
"M",
"M",
"USD",
"US Dollar",
485729103
],
[
"CCCREDIT",
"APPROVED",
-25.00,
485729104,
9820193,
"xtLCust88",
8819203,
"INV-882",
88292,
"Inovio Primary",
110203,
"Visa",
3920192,
"1111",
"",
"",
"REF291",
"C14D3C15-11B6-5280-B0E05G1DEE87208E",
"REF847156509",
"",
"",
"USD",
"US Dollar",
485729104
],
[
"CCCHARGEBACK",
"APPROVED",
-100.50,
485729105,
9820193,
"xtLCust88",
8819203,
"INV-882",
88293,
"Inovio Primary",
110203,
"Visa",
3920192,
"1111",
"",
"",
"",
"",
"",
"",
"",
"USD",
"US Dollar",
485729105
]
]
}
```
---
# ACH / eCheck API
Process electronic funds transfers directly from customer bank accounts, including the ACH transaction lifecycle, supported actions, request parameters, and status values.
Source: https://developer.inoviopay.com/api/ach.html
Markdown: https://developer.inoviopay.com/api/ach.md
## ACH & eCheck Transactions
Process electronic funds transfers directly from customer bank accounts. ACH authorization requests will typically return a `PENDING` status while the funds clear through the banking network.
The following steps describe the lifecycle of a transaction processed with an ACH account:
1. **Customer initiates purchase** on the payment page.
2. **Merchant submits transaction request** to the Payments Service. This should include the following data:
- Customer and payment information.
- Merchant gateway credentials and other gateway parameters as needed.
3. **Payments Service transmits** the transaction request to the bank.
4. **Merchant receives gateway response.** The transaction status should be `PENDING`.
5. **Post-processing transaction status update** (both methods are optional):
- **Postback API:** Payments Service sends a Postback to the merchant as the transaction state changes. Postbacks are real-time.
- **Order Detail Report:** Merchant downloads order and transaction data using the Order Detail Report service.
> **Note on status updates**
> If you choose not to use the Postback API or Order Detail Report to receive updates on the status of an ACH order, you can log into our portal and check the status manually by searching for the order ID.
>
> To keep merchants updated on the status of their pending ACH transactions, we can send realtime Postbacks to the merchant.
>
> Merchants may also download transaction status updates as an alternative to Postback by using Order Detail Report service.
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Set to `ACHAUTHCAP` for Auth/Capture, or `ACHAUTHORIZE` for validation without capture. |
| `PMT_NUMB` (required) | Customer Bank Account Number. |
| `BANK_IDENTIFIER` (required) | Bank Routing Number. |
| `LI_VALUE_1` (required) | Transaction Amount. |
## ACH Actions
Below are the only supported gateway actions for ACH. Any other gateway action or feature will not work.
| REQUEST_ACTION | Description |
|---|---|
| `ACHAUTHCAP` | Used for authorization and capture requests. |
| `ACHAUTHORIZE` | Used for Authorizations without Capture. |
| `ACHREVERSE` | Used for Authorization Capture Reversal. |
| `ACHCREDIT` | Used for transaction credit requests. |
| `ACHPAYOUT` | Used for ACH direct deposit payout. |
### Handling ACH Responses
Because ACH transactions rely on the banking network to clear funds, initial responses will typically return a `PENDING` status rather than an immediate approval or decline.
| Field Name | Description |
|---|---|
| `TRANS_STATUS_NAME` | Check this to ensure the transaction is `PENDING` (or `APPROVED` if the bank clears it instantly). |
| `TRANS_VALUE` | The amount requested for the transaction. |
| `TRANS_ID` | The unique Transaction ID for the gateway event. |
| `PO_ID` | The Purchase Order ID linking to the transaction. |
| `PROC_AUTH_RESPONSE` | The authorization string generated by the processor. |
### Auth & capture example
> **Not in the SDKs yet**
> ACH is not implemented in any of the four SDKs. `BankAccount` exists as a declared-but-unconstructible payment method variant (`PaymentMethods` only builds `card`, `token`, `savedCard` in v1; a `BankAccount` reaches the wire only by hand-assembling parameters outside the client), and none of `sale()`/`authorize()`/`reverse()`/`forceCredit()` recognize `ACHAUTHCAP`, `ACHAUTHORIZE`, `ACHREVERSE`, `ACHCREDIT` or `ACHPAYOUT` as actions, those five constants appear only as decorative labels in the generated enum files, not as request builders. Use the cURL example directly, or see [the SDKs](https://developer.inoviopay.com/sdks/index.md) for the v1 card surface.
### Authorize only example
> **Not in the SDKs yet**
> No SDK builds an `ACHAUTHORIZE` request. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
### Reversal example
> **Not in the SDKs yet**
> `reverse()` exists in all four SDKs, but it only builds `CCREVERSE` (card reversal), there is no `ACHREVERSE` path. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
### Credit example
> **Not in the SDKs yet**
> `forceCredit()` exists in all four SDKs, but it only builds `CCCREDIT` (card credit), there is no `ACHCREDIT` path. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
### Payout example
> **Not in the SDKs yet**
> `ACHPAYOUT` has no SDK equivalent in any of the four languages. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=ACHAUTHCAP&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=1234567890&bank_identifier=987654321&li_value_1=50.00"
```
**Response**
```json
{
"REQUEST_ACTION": "ACHAUTHCAP",
"REQ_ID": "67392811",
"TRANS_STATUS_NAME": "PENDING",
"TRANS_VALUE": 50.00,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 50.00,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572115,
"CUST_ID": 9827163,
"XTL_CUST_ID": "",
"PO_ID": 99281736,
"XTL_ORDER_ID": "",
"BATCH_ID": 883925,
"PROC_NAME": "Inovio ACH Primary",
"MERCH_ACCT_ID": 141630,
"CARD_BRAND_NAME": "ACH",
"CARD_TYPE": "",
"CARD_PREPAID": 0,
"CARD_BANK": "",
"CARD_DETAIL": "",
"CARD_BALANCE": "",
"PMT_L4": "7890",
"PMT_ID": 8839210,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "PEND992",
"PROC_RETRIEVAL_NUM": "A1B2C3D4-E5F6-7A8B-9C0D-E1F2A3B4C5D6",
"PROC_REFERENCE_NUM": "REF88291039",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928178",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "50.00",
"PO_LI_PROD_ID_1": "136381",
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=ACHAUTHORIZE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=1234567890&bank_identifier=987654321&li_value_1=50.00"
```
**Response**
```json
{
"REQUEST_ACTION": "ACHAUTHORIZE",
"REQ_ID": "67392812",
"TRANS_STATUS_NAME": "PENDING",
"TRANS_VALUE": 50.00,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 50.00,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572116,
"CUST_ID": 9827163,
"PO_ID": 99281737,
"BATCH_ID": 883925,
"PROC_NAME": "Inovio ACH Primary",
"MERCH_ACCT_ID": 141630,
"CARD_BRAND_NAME": "ACH",
"PMT_L4": "7890",
"PMT_ID": 8839210,
"PROC_AUTH_RESPONSE": "PEND993",
"PROC_RETRIEVAL_NUM": "B2C3D4E5-F6A7-8B9C-0D1E-2F3A4B5C6D7E",
"REQUEST_API_VERSION": "4.14",
"PO_LI_ID_1": "9928179",
"PO_LI_AMOUNT_1": "50.00"
}
```
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=ACHREVERSE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id=99281736"
```
**Response**
```json
{
"REQUEST_ACTION": "ACHREVERSE",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": -10,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": -10,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572120,
"CUST_ID": 9827164,
"XTL_CUST_ID": "",
"PO_ID": 99281739,
"XTL_ORDER_ID": "",
"BATCH_ID": 883926,
"PROC_NAME": "ACHProcessor",
"MERCH_ACCT_ID": 141630,
"CARD_BRAND_NAME": "",
"CARD_DETAIL": "",
"CARD_TYPE": "",
"CARD_PREPAID": "",
"CARD_BANK": "",
"PMT_L4": "4567",
"PMT_ID": 8839211,
"PMT_ID_XTL": "",
"PROC_UDF01": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "",
"PROC_RETRIEVAL_NUM": "",
"PROC_REFERENCE_NUM": "",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "",
"CVV_RESPONSE": "",
"REQ_ID": "84729106",
"REQUEST_API_VERSION": "4.14",
"PO_LI_ID_1": "9928183",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": -10,
"PO_LI_PROD_ID_1": "12345",
"MBSHP_ID_1": ""
}
```
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=ACHCREDIT&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_po_id=99281736&li_value_1=15.00"
```
**Response**
```json
{
"REQUEST_ACTION": "ACHCREDIT",
"TRANS_STATUS_NAME": "",
"TRANS_VALUE": "",
"TRANS_ID": "",
"REQ_ID": "84729105",
"CUST_ID": "9928104",
"XTL_CUST_ID": "xT883mP",
"PMT_ID": "",
"MERCH_ACCT_ID": "141630",
"CARD_BRAND_NAME": "",
"PMT_L4": "",
"API_RESPONSE": "0",
"API_ADVICE": " ",
"SERVICE_RESPONSE": 512,
"SERVICE_ADVICE": "Order not found",
"PROCESSOR_RESPONSE": 0,
"PROCESSOR_ADVICE": " ",
"INDUSTRY_RESPONSE": 0,
"INDUSTRY_ADVICE": " ",
"REF_FIELD": "",
"PROC_NAME": "",
"AVS_RESPONSE": "",
"CVV_RESPONSE": "",
"PROC_REDIRECT_URL": "",
"REQUEST_API_VERSION": "4.14",
"TRANS_NTOKEN_USED": 0
}
```
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=ACHPAYOUT&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=1234567890&bank_identifier=987654321&li_value_1=250.00"
```
**Response**
```json
{
"REQUEST_ACTION": "ACHPAYOUT",
"TRANS_STATUS_NAME": "",
"TRANS_VALUE": "",
"TRANS_ID": "",
"REQ_ID": "84729104",
"CUST_ID": 9928103,
"XTL_CUST_ID": "xT883mP",
"PMT_ID": 8829103,
"MERCH_ACCT_ID": "110203",
"CARD_BRAND_NAME": "ACH",
"PMT_L4": "7890",
"API_RESPONSE": "0",
"API_ADVICE": " ",
"SERVICE_RESPONSE": 522,
"SERVICE_ADVICE": "Unsupported card brand",
"PROCESSOR_RESPONSE": 0,
"ANI_RESP_DECISION": "",
"PROCESSOR_ADVICE": " ",
"INDUSTRY_RESPONSE": 0,
"INDUSTRY_ADVICE": " ",
"REF_FIELD": "",
"PROC_NAME": "",
"AVS_RESPONSE": "",
"CVV_RESPONSE": "",
"PROC_REDIRECT_URL": "",
"REQUEST_API_VERSION": "4.14",
"TRANS_NTOKEN_USED": 0
}
```
## ACH Request Parameters
The table below describes parameters needed in sending ACH purchase request to the gateway. Merchants may send additional parameters, as described in the Payments Payment Service API, such as customer billing address, email, website login and other pass-through data.
| Field Name | Description |
|---|---|
| `SITE_ID` | Merchant's Website ID. |
| `LI_PROD_ID_1` | Line Item Product ID 1. |
| `LI_VALUE_1` | Line Item Transaction Amount 1. |
| `PMT_NUMB` | Bank Account Number. |
| `BANK_IDENTIFIER` | Routing Number. |
| `MERCH_ACCT_ID` | Merchant Account ID. |
| `REQUEST_CURRENCY` | 3-letter Currency Code. |
| `CUST_FNAME` | Customer First Name. |
| `CUST_LNAME` | Customer Last Name. |
| `BILL_ADDR` | Billing Street Address. |
| `BILL_ADDR_CITY` | Billing City Name. |
| `BILL_ADDR_STATE` | Billing State 2-letter Code. |
| `BILL_ADDR_COUNTRY` | Billing Country 2-letter Code. |
| `BILL_ADDR_ZIP` | Billing ZIP or Postal Code. |
## ACH Transaction Status
| TRANS_STATUS_NAME | Description |
|---|---|
| `APPROVED` | Transaction has been approved. |
| `PENDING` | Transaction is in pending status. |
| `RUNNING` | Transaction processing was not completed or is waiting completion usually because of gateway error. |
---
# Partial Authorization
Accept an authorization for less than the full requested amount when a prepaid or debit card can't cover the order total.
Source: https://developer.inoviopay.com/api/partial-auth.html
Markdown: https://developer.inoviopay.com/api/partial-auth.md
Partial Authorization is a processor-specific feature that allows merchants to accept an authorization for a portion of the total requested amount. This is common when customers use prepaid or debit cards with a balance lower than the order total.
### How it Works
- You submit a standard `CCAUTHCAP` or `CCAUTHORIZE` request.
- You include the `PARTIAL_AUTH` flag and define a `PARTIAL_AUTH_MIN` (the lowest amount you are willing to accept to proceed).
- If the card has at least the minimum amount, the gateway returns an `APPROVED` status.
| Field Name | Description |
|---|---|
| PARTIAL_AUTH | Set to `1` to enable. Set to `0` or omit to disable. |
| PARTIAL_AUTH_MIN | The minimum numeric amount you will accept (e.g., `10.00`). If the card balance is below this, the transaction is declined. |
> **Critical Implementation Note**
> When Partial Authorization is enabled, you **must** parse the `TRANS_VALUE` field in the response. Do not assume an `APPROVED` status means the full amount was captured. Your system must handle the remaining balance via a secondary payment method.
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHCAP&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=4111111111111111&pmt_expiry=122026&pmt_key=123&request_currency=USD&li_value_1=75.00&xtl_order_id=INV-999&partial_auth=1&partial_auth_min=10.00"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PartialAuth, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = (new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[new LineItem('SKU-992', 1, Money::of('75.00', 'USD'))]
))->withIdempotency('INV-999');
$req->partialAuth = new PartialAuth(enabled: true);
$req->partialAuth->minimumAmount = Money::of('10.00', 'USD');
$result = $client->sale($req);
match ($result->status) {
'APPROVED' => /* $result->raw['TRANS_VALUE'] may be less than 75.00 — reconcile the remainder */,
'DECLINED' => /* card balance was below PARTIAL_AUTH_MIN */,
default => /* PENDING | RUNNING | FAILED */,
};
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const result = await client.sale({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [{ productId: 'SKU-992', count: 1, value: Money.of('75.00', 'USD') }],
idempotency: { xtlOrderId: 'INV-999' },
partialAuth: { enabled: true, minimumAmount: Money.of('10.00', 'USD') },
});
switch (result.status) {
case 'APPROVED': /* result.raw.TRANS_VALUE may be less than 75.00 */ break;
case 'DECLINED': /* card balance was below the minimum */ break;
default: /* PENDING | RUNNING | FAILED */ break;
}
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods, TransactionStatus
from inovio_gateway.model import Idempotency, LineItem, PartialAuth
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", site_id="12345"), environment="SANDBOX")
result = client.sale(TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[LineItem("SKU-992", 1, Money.of("75.00", "USD"))],
idempotency=Idempotency(xtl_order_id="INV-999"),
partial_auth=PartialAuth(enabled=True, minimum_amount=Money.of("10.00", "USD")),
))
if result.status is TransactionStatus.APPROVED:
pass # result.raw["TRANS_VALUE"] may be less than 75.00
elif result.status is TransactionStatus.DECLINED:
pass # card balance was below the minimum
```
**Java**
```java
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("SKU-992", 1, Money.of("75.00", "USD")))
.idempotency("INV-999");
RequestParts.PartialAuth partialAuth = new RequestParts.PartialAuth(true);
partialAuth.minimumAmount = Money.of("10.00", "USD");
req.partialAuth = partialAuth;
TransactionResult result = client.sale(req);
switch (result.status()) {
case APPROVED -> { /* result.raw().get("TRANS_VALUE") may be less than 75.00 */ }
case DECLINED -> { /* card balance was below the minimum */ }
default -> { /* PENDING | RUNNING | FAILED */ }
}
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHCAP",
"REQ_ID": "68192033",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 32.50,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 32.50,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 8839201112,
"CUST_ID": 9928102,
"XTL_CUST_ID": "cUsT992xP",
"PO_ID": 77281920,
"XTL_ORDER_ID": "INV-999",
"BATCH_ID": 882910,
"PROC_NAME": "Inovio Primary",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Mastercard",
"CARD_TYPE": "MASTERCARD BLACK CARD",
"CARD_CLASS": "Consumer Credit",
"CARD_PREPAID": 0,
"CARD_BANK": "CREDOMATIC INTERNATIONAL",
"CARD_COUNTRY": "CRI",
"CARD_DETAIL": "Credit",
"CARD_BALANCE": "",
"PMT_L4": "3535",
"PMT_ID": 8829102,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "AUTH99",
"PROC_RETRIEVAL_NUM": "1029384A-B8C7-D6E5-F4G3-H2I1J0K9L8M7",
"PROC_REFERENCE_NUM": "REF10293847",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "8829103",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "49.99",
"PO_LI_PROD_ID_1": "SKU-992",
"MBSHP_ID_1": "88291",
"TRANS_NTOKEN_USED": 1
}
```
---
# Card on File (Recurring)
Charge a previously saved payment method by Customer ID without resubmitting the card number.
Source: https://developer.inoviopay.com/api/card-on-file.html
Markdown: https://developer.inoviopay.com/api/card-on-file.md
To charge a customer using a previously saved payment method, you do not need to submit the full credit card number again. Instead, you can make a call without the card number by using only the Customer ID (`CUST_ID`). This will use the most recent payment account or credit card that was used.
The merchant may also specify which payment method to use by sending Customer ID (`CUST_ID`) paired with either the Payment ID (`PMT_ID`) or the last 4 digits of the card (`PMT_L4`).
| Parameter | Description |
|---|---|
| `CUST_ID` (required) | The Customer ID generated from the original authorization. |
| `PMT_ID / PMT_L4` (required) | Provide *either* the unique Payment ID (`PMT_ID`) or the last 4 digits of the card (`PMT_L4`) to specify which saved account to charge. |
| `REQUEST_REBILL` (optional) | Set to `1` to explicitly flag the transaction as a rebill/renewal. Set to `2` for the first transaction in a subscription. |
All four SDKs model a saved payment method as `SavedCard` (constructed with `pmtId` and/or `pmtIdXtl`, plus `custId`) and the `REQUEST_REBILL` / `REQUEST_INITATOR` compliance flags as a `Recurring` block on the request. `PMT_L4` card selection (the cURL example below) is not modeled as an SDK constructor; use `pmtId` from a prior result's `PMT_ID` instead.
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHCAP&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&cust_id=987654&pmt_l4=1111&li_value_1=25.00&request_rebill=1"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods, Recurring};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = new TransactionRequest(
PaymentMethods::savedCard(pmtId: '8839213', custId: '987654'),
[new LineItem('111205', 1, Money::of('25.00', 'USD'))]
);
$req->recurring = new Recurring();
$req->recurring->rebill = 'REBILL'; // REQUEST_REBILL=1 — a renewal, not the first charge
$result = $client->sale($req);
match ($result->status) {
'APPROVED' => /* funds captured against the saved card */,
'DECLINED' => /* $result->serviceClassification->stopRecurring — stop the dunning cycle? */,
default => /* PENDING | RUNNING | FAILED */,
};
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const result = await client.sale({
paymentMethod: PaymentMethods.savedCard({ pmtId: '8839213', custId: '987654' }),
lineItems: [{ productId: '111205', count: 1, value: Money.of('25.00', 'USD') }],
recurring: { rebill: 'REBILL' }, // REQUEST_REBILL=1 — a renewal, not the first charge
});
switch (result.status) {
case 'APPROVED': /* funds captured against the saved card */ break;
case 'DECLINED': /* result.serviceClassification?.stopRecurring — stop the dunning cycle? */ break;
default: /* PENDING | RUNNING | FAILED */ break;
}
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods, TransactionStatus
from inovio_gateway.model import LineItem, Recurring
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", site_id="12345"), environment="SANDBOX")
result = client.sale(TransactionRequest(
payment_method=PaymentMethods.saved_card(pmt_id="8839213", cust_id="987654"),
line_items=[LineItem("111205", 1, Money.of("25.00", "USD"))],
recurring=Recurring(rebill="REBILL"), # REQUEST_REBILL=1 — a renewal, not the first charge
))
if result.status is TransactionStatus.APPROVED:
pass # funds captured against the saved card
elif result.status is TransactionStatus.DECLINED:
pass # result.service_classification.stop_recurring — stop the dunning cycle?
```
**Java**
```java
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
TransactionRequest req = new TransactionRequest(
PaymentMethods.savedCard("8839213", null, "987654"),
new LineItem("111205", 1, Money.of("25.00", "USD")));
RequestParts.Recurring recurring = new RequestParts.Recurring();
recurring.rebill = "REBILL"; // REQUEST_REBILL=1 — a renewal, not the first charge
req.recurring = recurring;
TransactionResult result = client.sale(req);
switch (result.status()) {
case APPROVED -> { /* funds captured against the saved card */ }
case DECLINED -> { /* result.serviceClassification().stopRecurring() — stop the dunning cycle? */ }
default -> { /* PENDING | RUNNING | FAILED */ }
}
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHCAP",
"REQ_ID": "84729108",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 0,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 0,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572122,
"CUST_ID": 9827166,
"XTL_CUST_ID": "",
"PO_ID": 99281741,
"XTL_ORDER_ID": "",
"BATCH_ID": 883928,
"PROC_NAME": "Test Processor",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Visa",
"CARD_TYPE": "",
"CARD_CLASS": "",
"CARD_PREPAID": 0,
"CARD_BANK": "",
"CARD_COUNTRY": "",
"CARD_DETAIL": "",
"CARD_BALANCE": "",
"PMT_L4": "2345",
"PMT_ID": 8839213,
"PMT_ID_XTL": "",
"PMT_AAU_UPDATE_DT": "",
"PMT_AAU_UPDATE_DESC": "",
"PROC_UDF01": "",
"ANI_RESP_DECISION": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "TEST18263",
"PROC_RETRIEVAL_NUM": "803EECE4-7F22-4837-841C615C7D1E59D4",
"PROC_REFERENCE_NUM": "TEST469812895",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"CARD_BRAND_TRANSID": "",
"REQUEST_API_VERSION": "4.14",
"P3DS_VENDOR": "",
"P3DS_RESPONSE": "",
"PO_LI_ID_1": "9928185",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "0",
"PO_LI_PROD_ID_1": "111205",
"MBSHP_ID_1": "",
"TRANS_NTOKEN_USED": 0
}
```
---
# Multiple Line Items
Submit purchase orders with more than one line item using the indexed LI_ parameters.
Source: https://developer.inoviopay.com/api/line-items.html
Markdown: https://developer.inoviopay.com/api/line-items.md
The Payment Service supports purchase orders with more than one line item. Use the Line Item Parameters: `LI_PROD_ID_X`, `LI_COUNT_X`, and `LI_VALUE_X`. The "X" indicates a dynamic number depending on how many line items you want to send.
| Parameter | Description |
|---|---|
| `LI_PROD_ID_X` (required) | Product ID for the line item. Replace 'X' with an incrementing integer. |
| `LI_COUNT_X` (required) | Quantity for the line item. Max value is 99. |
| `LI_VALUE_X` (required) | Transaction Amount for the line item. |
Capturing a single line item later (partial shipment, split fulfillment) uses `captureLineItem(order, lineItemRef, amount)` in all four SDKs, taking a `LineItemRef` from `result.lineItemRefs` (or `result.line_item_refs` in Python) rather than a raw `PO_LI_ID` string.
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHORIZE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=4111111111111111&pmt_expiry=122026&pmt_key=123&request_currency=USD&li_prod_id_1=1001&li_value_1=19.95&li_prod_id_2=2001&li_value_2=10.50"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[
new LineItem('1001', 1, Money::of('19.95', 'USD')),
new LineItem('2001', 1, Money::of('10.50', 'USD')),
]
);
$result = $client->authorize($req);
match ($result->status) {
'APPROVED' => /* $result->lineItemRefs — one LineItemRef per LI_, for later captureLineItem() */,
'DECLINED' => /* $result->outcome->service */,
default => /* PENDING | RUNNING | FAILED */,
};
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const result = await client.authorize({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [
{ productId: '1001', count: 1, value: Money.of('19.95', 'USD') },
{ productId: '2001', count: 1, value: Money.of('10.50', 'USD') },
],
});
switch (result.status) {
case 'APPROVED': /* result.lineItemRefs — one LineItemRef per LI_ */ break;
case 'DECLINED': /* result.outcome.service */ break;
default: /* PENDING | RUNNING | FAILED */ break;
}
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods, TransactionStatus
from inovio_gateway.model import LineItem
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", site_id="12345"), environment="SANDBOX")
result = client.authorize(TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[
LineItem("1001", 1, Money.of("19.95", "USD")),
LineItem("2001", 1, Money.of("10.50", "USD")),
],
))
if result.status is TransactionStatus.APPROVED:
pass # result.line_item_refs — one LineItemRef per LI_, for later capture_line_item()
elif result.status is TransactionStatus.DECLINED:
pass # result.outcome.service
```
**Java**
```java
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("1001", 1, Money.of("19.95", "USD")),
new LineItem("2001", 1, Money.of("10.50", "USD")));
TransactionResult result = client.authorize(req);
switch (result.status()) {
case APPROVED -> { /* result.lineItemRefs() — one LineItemRef per LI_ */ }
case DECLINED -> { /* result.outcome().service() */ }
default -> { /* PENDING | RUNNING | FAILED */ }
}
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHORIZE",
"TRANS_STATUS_NAME": "APPROVED",
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE": 30.45,
"TRANS_VALUE_SETTLED": 30.45,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572123,
"CUST_ID": 9827167,
"XTL_CUST_ID": "",
"PO_ID": 99281742,
"XTL_ORDER_ID": "",
"BATCH_ID": 883929,
"PROC_NAME": "Test Processor",
"MERCH_ACCT_ID": 100,
"CARD_BRAND_NAME": "Visa",
"CARD_DETAIL": "CREDIT",
"CARD_TYPE": "VISA BUSINESS",
"CARD_CLASS": "CONSUMER CREDIT",
"CARD_COUNTRY": "CRI",
"CARD_PREPAID": 0,
"CARD_BANK": "",
"CARD_BALANCE": "",
"PMT_L4": "1111",
"PMT_ID": "",
"PMT_ID_XTL": "",
"PROC_UDF01": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "AUTH514",
"PROC_RETRIEVAL_NUM": "7169CEF3-64EF-46BB-55A333956994D2D",
"PROC_REFERENCE_NUM": "",
"REQ_ID": "84729109",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"REQUEST_API_VERSION": "4.14",
"PO_LI_ID_1": "9928186",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": 19.95,
"PO_LI_PROD_ID_1": "1001",
"PO_LI_ID_2": "9928187",
"PO_LI_COUNT_2": 1,
"PO_LI_AMOUNT_2": 10.5,
"PO_LI_PROD_ID_2": "2001",
"MBSHP_ID_1": ""
}
```
---
# Memberships & Subscriptions
Create, update, and cancel subscription-based products through the API.
Source: https://developer.inoviopay.com/api/memberships.html
Markdown: https://developer.inoviopay.com/api/memberships.md
The gateway provides full lifecycle management for subscription-based products. Subscriptions can be defined either in the portal or directly through the API during the initial authorization.
### Direct Creation Parameters
| Parameter | Description |
|---|---|
| `PROD_NAME` | Name for the new subscription product. |
| `PROD_TYPE` | 1 = Cancels after period; 2 = Auto-renews. |
| `PROD_REBILL_METRIC` | Interval type: M (Month), D (Day), Y (Year). |
| `PROD_REBILL_PERIOD` | Numeric interval (e.g., 30 for monthly). |
### Management Actions (Cancel & Update)
Use the following actions to manage an existing membership record.
| Parameter | Description |
|---|---|
| `REQUEST_ACTION` (required) | Must be set to `SUB_CANCEL` or `SUB_UPDATE`. |
| `REQUEST_REF_MBSHP_ID` (required) | The existing Membership ID you wish to modify. |
| `SUB_CANCEL_TYPE` (optional) | Used with `SUB_CANCEL`. Set to `1` to cancel immediately, or `2` to cancel on the next scheduled rebill date. |
| `SUB_UPDATE_PROD_ID` (optional) | Used with `SUB_UPDATE` to change the current product ID of the membership record. |
| `SUB_UPDATE_PMT_ID` (optional) | Used with `SUB_UPDATE` to change the credit card/payment method tied to the specific membership. |
### Handling the Response
When updating a membership, ensure the `SERVICE_RESPONSE` indicates success (e.g., `102` for "Membership Updated") and check the `MBSHP_REBILL_TS_UTC` to confirm the next billing cycle date.
| Field Name | Description |
|---|---|
| `API_RESPONSE / SERVICE_RESPONSE` | Check these values for `0` or `102` to verify the request was successfully processed. |
| `MBSHP_ID` | The unique Membership ID that was modified or cancelled. |
| `MBSHP_REBILL_TS_UTC` | The UTC timestamp of when the next billing cycle will occur. |
| `MBSHP_CANCEL_TS_UTC` | The UTC timestamp of when the cancellation is scheduled to take effect (if applicable). |
| `CUST_ID` | The Customer ID tied to the membership. |
### Update membership example
> **Not in the SDKs yet**
> Membership/subscription management is not implemented in any of the four SDKs. `SUB_UPDATE` and `SUB_CANCEL` exist only as labels in the generated `RequestAction` enum; no client method builds either request, and none of `sale()`/`authorize()`/`capture()` accept an arbitrary `REQUEST_ACTION` override. Use the cURL example directly, or see [the SDKs](https://developer.inoviopay.com/sdks/index.md).
### Cancel membership example
> **Not in the SDKs yet**
> Same as above: no SDK builds a `SUB_CANCEL` request. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=SUB_UPDATE&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_mbshp_id=83262&sub_update_prod_id=99281"
```
**Response**
```json
{
"REQUEST_ACTION": "SUB_UPDATE",
"API_RESPONSE": "0",
"API_ADVICE": "",
"SERVICE_RESPONSE": 102,
"SERVICE_ADVICE": "Membership Updated",
"REF_FIELD": "",
"CUST_ID": 8076911,
"MBSHP_ID": 83262,
"MBSHP_CANCEL_TS_UTC": "",
"MBSHP_REBILL_TS_UTC": "March, 25 2026 19:34:39"
}
```
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=SUB_CANCEL&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&request_ref_mbshp_id=92831&sub_cancel_type=2"
```
**Response**
```json
{
"REQUEST_ACTION": "SUB_CANCEL",
"API_RESPONSE": "0",
"API_ADVICE": "",
"SERVICE_RESPONSE": 0,
"SERVICE_ADVICE": "",
"REF_FIELD": "",
"CUST_ID": 7283921,
"MBSHP_ID": 92831,
"MBSHP_CANCEL_TS_UTC": "April, 30 2026 15:30:35",
"MBSHP_REBILL_TS_UTC": ""
}
```
---
# Fraud Mitigation
Control AVS and CVV checks per transaction, overriding the portal-level defaults.
Source: https://developer.inoviopay.com/api/fraud-mitigation.html
Markdown: https://developer.inoviopay.com/api/fraud-mitigation.md
Inovio provides built-in validation for Address Verification Service (AVS) and Card Security Code (CVV). By default, the gateway performs these checks but ignores the results unless specific flags are enabled. This can be maintained in the Inovio Portal settings.
If you want to manually control AVS and CVV settings or override the portal settings for specific transactions you can send the following parameters.
### Address Verification Service (AVS)
AVS verifies the billing address provided by the customer against the address on file at the issuing bank. It is generally supported in the US and several international markets.
| Parameter | Setting | Description |
|---|---|---|
| `CHKAVS` (optional) | `T` | **Enabled:** Transaction is approved only if the response matches the [AVSMatchSet](https://developer.inoviopay.com/reference/avs-codes.md). |
| `CHKAVS` (optional) | `F` | **Disabled:** Do not perform AVS check. |
| `CHKAVS` (optional) | `C` | **Conditional:** Check AVS, but ignore results if the CVV check is a match. |
| `AVSMATCHSET` (optional) | `[Codes]` | A custom string of AVS codes to accept (e.g., `ADRSTUVWZ`). If the bank returns a code not in this set, the transaction is declined. |
### Card Security Code (CVV)
The Card Security Code (CVV2/CVC2) is the 3 or 4-digit number on the card. This must be submitted in the `PMT_KEY` parameter.
| Parameter | Setting | Description |
|---|---|---|
| `CHKCVV` (optional) | `T` | **Enabled:** Transaction is approved only if the response matches the [CVVMATCHSET](https://developer.inoviopay.com/reference/cvv-codes.md). |
| `CHKCVV` (optional) | `F` | **Disabled:** Do not send CVV to the bank. |
| `CHKCVV` (optional) | `C` | **Conditional:** Check CVV, but ignore results if the AVS check is a match. |
| `CVVMATCHSET` (optional) | `[Codes]` | A custom string of CVV codes to accept (e.g., `MPSX`). Default positive response is `M` (Match). |
AVS and CVV enforcement is layered onto whatever request action you're already sending (`CCAUTHORIZE`, `CCAUTHCAP`), it does not require a separate SDK call, only additional parameters on the existing sale/authorize request.
All four SDKs model these as a `RiskOptions` block (`avs` / `cvv`, values `on` / `off` / `ignore` / `conditional`) on the transaction request, and expose the response as `avs.classification` / `cvv.classification` (`positive` / `partial` / `negative` / `neutral`, derived by the SDK from `AVS_RESPONSE` / `CVV_RESPONSE`), not just the raw letter code.
> **The SDKs currently send the wrong CHKAVS / CHKCVV values**
> The gateway compares `CHKAVS` and `CHKCVV` against the letter codes in the table above (`T`, `F`, `C`, default `I`); this is verified against the gateway's order processing. All four SDKs instead encode `RiskOptions.avs` / `.cvv` as `1` (on), `0` (off), `2` (ignore) and `3` (conditional), which the gateway does not recognise, so setting `RiskOptions` through an SDK today has no effect and the account's default AVS/CVV policy applies. Until the SDKs are corrected, enforce AVS and CVV by calling the API directly with the letter codes, as in the cURL tab.
**cURL**
```bash
curl -X POST "https://api.inoviopay.com/payment/pmt_service.cfm" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "request_action=CCAUTHCAP&req_username=api_user&req_password=P%40ssw0rd%21&site_id=12345&request_api_version=4.14&request_response_format=JSON&pmt_numb=4111111111111111&pmt_expiry=122026&pmt_key=123&request_currency=USD&li_value_1=49.99&xtl_order_id=INV-999&CHKAVS=T&CHKCVV=T"
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods, RiskOptions};
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = (new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[new LineItem('111205', 1, Money::of('49.99', 'USD'))]
))->withIdempotency('INV-999');
$req->risk = new RiskOptions();
$req->risk->avs = 'on';
$req->risk->cvv = 'on';
$result = $client->sale($req);
if ($result->avs !== null) {
echo $result->avs->classification; // positive | partial | negative | neutral
}
if ($result->cvv !== null) {
echo $result->cvv->classification;
}
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
const result = await client.sale({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [{ productId: '111205', count: 1, value: Money.of('49.99', 'USD') }],
idempotency: { xtlOrderId: 'INV-999' },
risk: { avs: 'on', cvv: 'on' },
});
console.log(result.avs?.classification); // positive | partial | negative | neutral
console.log(result.cvv?.classification);
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, Money, PaymentMethods
from inovio_gateway.model import Idempotency, LineItem, RiskOptions
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", site_id="12345"), environment="SANDBOX")
result = client.sale(TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[LineItem("111205", 1, Money.of("49.99", "USD"))],
idempotency=Idempotency(xtl_order_id="INV-999"),
risk=RiskOptions(avs="on", cvv="on"),
))
if result.avs:
print(result.avs.classification) # positive | partial | negative | neutral
if result.cvv:
print(result.cvv.classification)
```
**Java**
```java
InovioClient client = new InovioClient(
new InovioClient.Credentials("api_user", "P@ssw0rd!", "12345"));
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("111205", 1, Money.of("49.99", "USD")))
.idempotency("INV-999");
RequestParts.RiskOptions risk = new RequestParts.RiskOptions();
risk.avs = "on";
risk.cvv = "on";
req.risk = risk;
TransactionResult result = client.sale(req);
if (result.avs() != null) {
System.out.println(result.avs().classification()); // positive | partial | negative | neutral
}
if (result.cvv() != null) {
System.out.println(result.cvv().classification());
}
```
**Response**
```json
{
"REQUEST_ACTION": "CCAUTHCAP",
"REQ_ID": "84729110",
"TRANS_STATUS_NAME": "APPROVED",
"TRANS_VALUE": 3.15,
"CURR_CODE_ALPHA": "USD",
"TRANS_VALUE_SETTLED": 3.15,
"CURR_CODE_ALPHA_SETTLED": "USD",
"TRANS_EXCH_RATE": "",
"TRANS_ID": 3948572124,
"CUST_ID": 9827168,
"XTL_CUST_ID": "xT883mP",
"PO_ID": 99281743,
"XTL_ORDER_ID": "",
"BATCH_ID": 883930,
"PROC_NAME": "Test Processor",
"MERCH_ACCT_ID": 110203,
"CARD_BRAND_NAME": "Visa",
"CARD_TYPE": "VISA CLASSIC",
"CARD_PREPAID": 1,
"CARD_BANK": "BOFI FEDERAL BANK",
"CARD_BALANCE": "",
"PMT_L4": "2921",
"PMT_ID": 8839214,
"PMT_ID_XTL": "",
"PROC_UDF01": "",
"PROC_UDF02": "",
"PROC_AUTH_RESPONSE": "TEST458",
"PROC_RETRIEVAL_NUM": "930C79ED-E6D2-428B-A63EF36A9D769685",
"PROC_REFERENCE_NUM": "TEST938316620",
"PROC_REDIRECT_URL": "",
"AVS_RESPONSE": "M",
"CVV_RESPONSE": "M",
"REQUEST_API_VERSION": "4.14",
"P3DS_RESPONSE": "",
"P3DS_VENDOR": "",
"PO_LI_ID_1": "9928188",
"PO_LI_COUNT_1": 1,
"PO_LI_AMOUNT_1": "3.15",
"PO_LI_PROD_ID_1": "111205",
"MBSHP_ID_1": ""
}
```
---
# Order Insight (Verifi)
Share transaction details with issuers in real time to resolve disputes and prevent chargebacks before they're filed.
Source: https://developer.inoviopay.com/api/order-insight.html
Markdown: https://developer.inoviopay.com/api/order-insight.md
> **Not in the SDKs yet**
> Order Insight is not implemented in any of the four SDKs. `XTL_CUST_ID`, `PROD_DESC_XTL`, `DEVICE_ID_XTL` and `DEVICE_FINGERPRINT_XTL` are not modeled on `Customer` or `Metadata` in any language SDK, so these fields must be sent as raw form parameters alongside an SDK-built request, or over plain HTTP as shown below. See [the SDKs](https://developer.inoviopay.com/sdks/index.md).
Order Insight is a real-time platform that allows merchants to share transaction details with issuers to resolve disputes and prevent chargebacks before they are formally filed. By providing granular data, you can prove transaction legitimacy through Compelling Evidence (CE).
### How it Works
When a customer initiates a dispute, Inovio automatically shares the transactional data you provided at the time of purchase with the issuer. This process can deflect potential chargebacks by clarifying the purchase for the cardholder and bank.
| Field Name | Description |
|---|---|
| XTL_CUST_ID | Merchant's internal Customer ID (Max 24 chars). |
| PROD_DESC_XTL | Detailed description of the merchandise or service (Up to 1,000 chars). |
| DEVICE_ID_XTL | The IMEI or MEID of the device used. Data must not be hashed. |
| DEVICE_FINGERPRINT_XTL | Unique fingerprint generated by your system to identify the device. |
> **Data Retention Requirements**
> Standard Order Insight requires a 120-day history of product parameters. To utilize Compelling Evidence (CE), you must maintain a 365-day history of customer and device parameters.
### Order Insight statuses
| Status | Meaning |
|---|---|
| Responded | Inovio successfully provided data to Verifi. |
| Deflection | The lookup prevented a chargeback. |
| Negation | The lookup resulted in a chargeback. |
| Failed | Inovio did not receive the request within SLA. |
| Reversal | A previously deflected dispute was reversed. |
---
# Timeout Void
Cap how long the gateway waits on a processor response and auto-void ghost approvals that arrive after the timeout.
Source: https://developer.inoviopay.com/api/timeout-void.html
Markdown: https://developer.inoviopay.com/api/timeout-void.md
The Timeout Void feature allows you to set a maximum duration for the gateway to wait for a processor's response on `CCAUTHORIZE` or `CCAUTHCAP` requests. If the gateway does not receive an end-state response within your specified window, it will return a decline to your system.
### Automated Resolution
Because the transaction may still be in-flight with the processor when the gateway times out, Inovio protects you from "ghost" approvals. If the processor eventually approves the transaction after the timeout period, the gateway will automatically submit a void request to ensure the cardholder is not charged for an abandoned transaction.
| Field Name | Description | Value Guideline |
|---|---|---|
| REQUEST_MAX_WAIT | Enables or disables the timeout functionality. | `1` (Enable) or `0` (Disable). |
| REQUEST_MAX_WAIT_TIMER | The number of seconds to wait before abandoning the request. | Required if enabled. Must be between `30` and `600` seconds. |
> **Global Portal Setting**
> Merchants can also set a global Timeout Void preference at the account level within the Inovio Portal. Any parameters sent within an individual API request will override the global portal settings.
**Request**
```
// Set a strict 60-second limit for this sale
REQUEST_ACTION=CCAUTHCAP
&REQUEST_MAX_WAIT=1
&REQUEST_MAX_WAIT_TIMER=60
&LI_VALUE_1=100.00...
```
**PHP**
```php
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Errors\GatewayTimeoutException;
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Refs\Refs;
use Inovio\Gateway\Request\TransactionRequest;
$client = new InovioClient(new Credentials('api_user', 'P@ssw0rd!', '12345'), 'SANDBOX');
$req = (new TransactionRequest(
PaymentMethods::card('4111111111111111', '122026', '123'),
[new LineItem('SKU-1', 1, Money::of('100.00', 'USD'))]
))->withIdempotency('ORDER-555');
try {
$client->sale($req);
} catch (GatewayTimeoutException $e) {
// The state is UNKNOWN, not failed — resolve it, don't retry blindly.
$actual = $client->status(Refs::xtlOrder($e->xtlOrderId()));
echo $actual->transactions === [] ? 'safe to retry' : 'already happened', "\n";
}
```
**Node**
```ts
import { InovioClient, Money, PaymentMethods, Refs, TimeoutError } from '@inovio/gateway-sdk';
const client = new InovioClient(
{ reqUsername: 'api_user', reqPassword: 'P@ssw0rd!', siteId: '12345' },
{ environment: 'SANDBOX' }
);
try {
await client.sale({
paymentMethod: PaymentMethods.card('4111111111111111', '122026', '123'),
lineItems: [{ productId: 'SKU-1', count: 1, value: Money.of('100.00', 'USD') }],
idempotency: { xtlOrderId: 'ORDER-555' },
});
} catch (e) {
if (!(e instanceof TimeoutError)) throw e;
// The state is UNKNOWN, not failed — resolve it, don't retry blindly.
const actual = await client.status(Refs.xtlOrder(e.xtlOrderId!));
console.log(actual.transactions.length === 0 ? 'safe to retry' : 'already happened');
}
```
**Python**
```python
from inovio_gateway import Credentials, InovioClient, InovioTimeoutError, Money, PaymentMethods, Refs
from inovio_gateway.model import Idempotency, LineItem
from inovio_gateway.request import TransactionRequest
client = InovioClient(Credentials("api_user", "P@ssw0rd!", "12345"), environment="SANDBOX")
req = TransactionRequest(
payment_method=PaymentMethods.card("4111111111111111", "122026", "123"),
line_items=[LineItem("SKU-1", 1, Money.of("100.00", "USD"))],
idempotency=Idempotency(xtl_order_id="ORDER-555"),
)
try:
client.sale(req)
except InovioTimeoutError as e:
# The state is UNKNOWN, not failed — resolve it, don't retry blindly.
actual = client.status(Refs.xtl_order(e.xtl_order_id))
print("safe to retry" if not actual.transactions else "already happened")
```
**Java**
```java
import com.inoviopay.gateway.errors.GatewayTimeoutException;
import com.inoviopay.gateway.refs.Refs;
import com.inoviopay.gateway.result.OrderStatus;
TransactionRequest req = new TransactionRequest(
PaymentMethods.card("4111111111111111", "122026", "123"),
new LineItem("SKU-1", 1, Money.of("100.00", "USD")))
.idempotency("ORDER-555");
try {
client.sale(req);
} catch (GatewayTimeoutException e) {
// The state is UNKNOWN, not failed — resolve it, don't retry blindly.
OrderStatus actual = client.status(Refs.xtlOrder(e.xtlOrderId()));
System.out.println(actual.transactions().isEmpty() ? "safe to retry" : "already happened");
}
```
**Response**
```json
{
"TRANS_STATUS_NAME": "DECLINED",
"SERVICE_RESPONSE": "626",
"SERVICE_ADVICE": "Authorization has been voided in accordance to timeout settings."
}
```
---
# 3D Secure: Inovio Gateway
The 5-step server-and-browser workflow for running 3D Secure 2.0 through the Inovio Gateway's device data collection and challenge flow.
Source: https://developer.inoviopay.com/api/3ds-gateway.html
Markdown: https://developer.inoviopay.com/api/3ds-gateway.md
> **SDK coverage of this flow**
> The PHP SDK owns every server leg of this flow: `$client->threeDSecure()->prepare()` (Step 1), the `ThreeDS` block on a normal `sale()`/`authorize()` (Step 3), and `completeSale()`/`completeAuthorize()` (Step 5). Node, Python and Java carry only `BrowserData` on the request (Step 3) and read the challenge outcome from `result.nextAction` (`kind: 'threeDSChallenge'`); none of the three expose a `prepare()`/DDC call or a completion-leg method, so Steps 1 and 5 have no SDK path in those three languages today. If you need the full gateway-managed flow outside PHP, drive the endpoints directly as shown in the cURL examples below, or see [the SDKs](https://developer.inoviopay.com/sdks/index.md).
This flow allows the Inovio Gateway to manage the 3D Secure 2.0 process. A successful 3DS implementation requires a strict sequence of events spanning your backend servers and the customer's browser. Below is the complete 5-step workflow.
### Step 1: Request Device Data Collection (DDC) Parameters
Before you can authorize the card, you must collect device profile data. Make a server-to-server request to the 3DS endpoint (`https://api.inoviopay.com/payment/3dsrequest.cfm`) to get your session parameters.
**Required Parameters:** `REQ_USERNAME`, `REQ_PASSWORD`, `MERCH_ACCT_ID`, and `PMT_BIN` (the first 6 digits of the customer's card), along with your standard billing and transaction details.
The response will contain a `JWT`, a `DDC_URL`, and a `DDC_REFERENCEID`. Store the Reference ID on your server; you will need it in Step 3.
### Step 2: Execute DDC on the Client Side
Device profiling **must** happen on the customer's device. Using the response from Step 1, create a hidden HTML form on your checkout page containing the `JWT` as an input field, and POST it targeting a hidden iframe to the `DDC_URL`.
> **Client-Side Execution Required**
> Do not attempt to POST the JWT to the DDC URL from your backend server. The provider must read the browser's user-agent and device footprint directly to satisfy the issuer's risk algorithms.
```html
```
### Step 3: Initial Enrollment Check
Once the DDC iframe has executed (typically wait 1-2 seconds or listen for the provider's javascript `postMessage`), submit your standard transaction request (e.g., `CCAUTHCAP` or `CCAUTHORIZE`) to the primary gateway endpoint.
You must include two additional parameters in this authorization call:
- `REQUEST_ENROLLMENT=1` (Flags this as a 3DS check)
- `DDC_REFERENCEID` (The exact ID returned in Step 1)
- `P3DS_RETURN_URL` (Where the customer should be sent after the challenge)
If the issuer determines the transaction is low-risk, they may grant a frictionless flow, returning a standard `APPROVED` response. If a challenge is required, the gateway will return a `PENDING` status along with the ACS URL and challenge payload parameters.
### Step 4: Presenting the Challenge (If Required)
If you receive a `PENDING` status, the cardholder's bank requires them to complete an authentication challenge (like entering an SMS code). The response will contain an `P3DS_ACS_URL` and a `P3DS_PAYLOAD`.
To display this challenge to the user without them leaving your checkout flow:
1. Create a visible `