# SDKs

Server-side SDKs for the Inovio gateway in PHP, Node, Python and Java, covering the v1 card surface against API v4.14.

Source: https://developer.inoviopay.com/sdks/index.html  
Markdown: https://developer.inoviopay.com/sdks/index.md

Four server-side libraries wrap the Inovio Gateway Payments Service so you call
`client.sale()` instead of assembling `REQUEST_ACTION=CCAUTHCAP` form fields by
hand. They are language-idiomatic projections of one object model: a partner
reading the Node docs recognises the PHP shape one for one.

All four target **API version 4.14** and cover the **v1 card surface**: sale,
authorize, capture, line-item capture, reverse, reverse capture, refund, force
credit, status, order update, tokenize, and the two health checks. Payment
methods are `Card`, `Token` and `SavedCard`. ACH, EU direct debit, LatAm
vouchers, wallets, subscriptions and disputes are modelled in the type system
but not implemented, so adding them later fills existing seams rather than
breaking your integration.

> **The SDKs are alpha**
> Version 0.1.0-alpha, and **not published to any package registry**. Install
> from the public GitHub repository, as shown below. Pin a commit until a
> tagged release lands.

## What the SDKs are for

Every one of them is a **server-side** library. They hold your gateway
credentials, sign token-service requests with your site key, and speak the
gateway's form-encoded protocol. None of them runs in a browser.

What you get over raw HTTP:

- **Actions become methods.** `client.refund(orderRef, amount)`, not a
  `REQUEST_ACTION` string plus six correlated parameters.
- **The five-state result.** `APPROVED`, `DECLINED`, `PENDING`, `RUNNING`,
  `FAILED`, with no `approved` boolean to make `PENDING` look like a failure.
- **Decimal money.** Amounts never pass through a binary float in any of the
  four languages.
- **Typed references.** `capture()` takes an `OrderRef`, so it cannot be handed
  a customer id by mistake.
- **Idempotency and timeout recovery.** Setting an order id makes a retry
  return the original result instead of charging twice, and the timeout
  exception carries the key you need to reconcile.
- **Wire quirks normalised once.** The `REQUEST_INITATOR` misspelling, the
  `XTL_ORDER_ID` and `XTL_PO_ID` duality, `PMT_L4` versus `PMT_LAST4`, and the
  case-inconsistent response keys never reach you.

See [How the SDKs think](https://developer.inoviopay.com/sdks/concepts.md) for the object model these share.

## Choosing a language

| | PHP | Node / TypeScript | Python | Java |
|---|---|---|---|---|
| Minimum runtime | PHP 8.0 | Node 18 | Python 3.8 | Java 11 |
| Extra dependencies | none (Composer) | none | none | none |
| Required extensions | `ext-json`, `ext-bcmath`, `ext-curl` | n/a | n/a | n/a |
| HTTP client | cURL, injectable | `fetch`, injectable | `urllib`, injectable | `java.net.http`, injectable |
| Amount type | bcmath decimal string | decimal string | `decimal.Decimal` | `BigDecimal` |
| Concurrency | synchronous | promise-based | synchronous | synchronous |
| Package name | `inovio/gateway-sdk` | `@inovio/gateway-sdk` | `inovio-gateway-sdk` | `com.inoviopay:inovio-gateway-sdk` |
| Version | 0.1.0-alpha | 0.1.0-alpha | 0.1.0-alpha | 0.1.0-alpha |
| Registry status | not on Packagist | not on npm | not on PyPI | not on Maven Central |
| 3D Secure | full server legs | `BrowserData` only | `BrowserData` only | `BrowserData` only |
| Repository | [inovio-gateway-sdk-php](https://github.com/Inoviopay/inovio-gateway-sdk-php) | [inovio-gateway-sdk-node](https://github.com/Inoviopay/inovio-gateway-sdk-node) | [inovio-gateway-sdk-python](https://github.com/Inoviopay/inovio-gateway-sdk-python) | [inovio-gateway-sdk-java](https://github.com/Inoviopay/inovio-gateway-sdk-java) |
| Page | [PHP](https://developer.inoviopay.com/sdks/php.md) | [Node](https://developer.inoviopay.com/sdks/node.md) | [Python](https://developer.inoviopay.com/sdks/python.md) | [Java](https://developer.inoviopay.com/sdks/java.md) |

The 3D Secure row is the one real capability difference. PHP ships the full
server-leg client (`prepare()`, the enrollment leg, `completeSale()`); the other
three carry the `BrowserData` block and read the challenge `nextAction` from a
result, but do not yet expose a `threeDSecure()` sub-client. If you need gateway
3DS today, use PHP or drive [the 3DS endpoints](https://developer.inoviopay.com/api/3ds-gateway.md) directly.

Node is the reference implementation. It defined the canonical method surface
and the conformance fixtures the other three run against, so where the four
disagree, Node is the intended shape.

## Install and first transaction

Each SDK installs from its public GitHub repository. Below is the install
command followed by a sale, in each language.


The gateway endpoint defaults to the sandbox
(`https://api-uap.inoviopay.com/payment/pmt_service.cfm`). Pass the
`PRODUCTION` environment to switch to `https://api.inoviopay.com/payment/pmt_service.cfm`,
or override the endpoint outright to point at a local stack or a proxy.

**PHP**

```php
# composer.json: add the repository, then require the package
composer config repositories.inovio vcs https://github.com/Inoviopay/inovio-gateway-sdk-php
composer require inovio/gateway-sdk:dev-main
use Inovio\Gateway\{Credentials, InovioClient};
use Inovio\Gateway\Model\{LineItem, Money, PaymentMethods};
use Inovio\Gateway\Request\TransactionRequest;

$client = new InovioClient(new Credentials($user, $password, '123'), 'SANDBOX');

$req = (new TransactionRequest(
    PaymentMethods::card('4111111111111111', '122030', '123'),
    [new LineItem('SKU-1', 1, Money::of('10.00', 'USD'))]
))->withIdempotency('ORDER-555');   // retry-safe by default

$result = $client->sale($req);

match ($result->status) {
    'APPROVED' => /* fulfil */,
    'DECLINED' => /* $result->outcome->service, $result->serviceClassification */,
    'PENDING'  => /* $result->nextAction — 3DS challenge, redirect, voucher */,
    default    => /* RUNNING | FAILED */,
};
```

**Node**

```ts
npm install github:Inoviopay/inovio-gateway-sdk-node
import { InovioClient, Money, PaymentMethods, Refs } from '@inovio/gateway-sdk';

const client = new InovioClient(
  { reqUsername: process.env.INOVIO_USER!, reqPassword: process.env.INOVIO_PASS!, siteId: '123' },
  { environment: 'SANDBOX' }
);

const result = await client.sale({
  paymentMethod: PaymentMethods.card('4111111111111111', '122030', '123'),
  lineItems: [{ productId: 'SKU-1', count: 1, value: Money.of('10.00', 'USD') }],
  idempotency: { xtlOrderId: 'ORDER-555' },   // retry-safe by default
});

switch (result.status) {
  case 'APPROVED': /* fulfil */ break;
  case 'DECLINED': /* result.outcome.service, result.serviceClassification */ break;
  case 'PENDING':  /* result.nextAction — 3DS challenge, redirect, voucher */ break;
  case 'RUNNING':
  case 'FAILED':   break;
}
```

**Python**

```python
pip install "git+https://github.com/Inoviopay/inovio-gateway-sdk-python.git@main"
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(user, password, site_id="123"), environment="SANDBOX")

result = client.sale(TransactionRequest(
    payment_method=PaymentMethods.card("4111111111111111", "122030", "123"),
    line_items=[LineItem("SKU-1", 1, Money.of("10.00", "USD"))],
    idempotency=Idempotency(xtl_order_id="ORDER-555"),   # retry-safe by default
))

if result.status is TransactionStatus.APPROVED:
    ...
elif result.status is TransactionStatus.PENDING:
    result.next_action     # 3DS challenge, redirect, voucher
```

**Java**

```java
git clone https://github.com/Inoviopay/inovio-gateway-sdk-java.git
cd inovio-gateway-sdk-java && mvn install
# then depend on com.inoviopay:inovio-gateway-sdk:0.1.0-alpha.0
InovioClient client = new InovioClient(
    new InovioClient.Credentials(user, password, "123"));

TransactionRequest req = new TransactionRequest(
    PaymentMethods.card("4111111111111111", "122030", "123"),
    new LineItem("SKU-1", 1, Money.of("10.00", "USD")))
    .idempotency("ORDER-555");          // retry-safe by default

TransactionResult r = client.sale(req);

switch (r.status()) {
    case APPROVED: /* fulfil */ break;
    case DECLINED: /* r.outcome().service(), r.serviceClassification() */ break;
    case PENDING:  /* r.nextAction() — 3DS challenge, redirect, voucher */ break;
    case RUNNING:
    case FAILED:   break;
}
```

## Where the card number goes

Every SDK's `tokenize()` is a **server-side** call. It POSTs the PAN to
`token_service.cfm` from your process, which means the card number transits
your infrastructure and your server sits inside your own cardholder data flow.
That is a deliberate property of this surface, not an oversight.

The lower-scope alternative is a browser client that tokenizes the PAN without
it ever reaching your server. **That Hosted Fields client is not yet
available.** Until it ships, your options are:

- **Server-side `tokenize()`**, accepting that the PAN passes through your
  server. See [Tokenization](https://developer.inoviopay.com/api/tokenization.md) for the endpoint contract.
- **Browser direct-post to `token_service.cfm`.** The token service sends
  `Access-Control-Allow-Origin: *`, so a browser can post the PAN to it
  directly once your server has supplied an HMAC signature. This is what the
  [cart plugins](https://developer.inoviopay.com/carts/index.md) do: the shopper's browser exchanges the PAN
  for a `TOKEN_GUID`, and only the token reaches the store's PHP. All four SDKs
  export the signing helper that a merchant-hosted signature endpoint needs.

The site key that signs a token request is a per-site HMAC secret issued by
Inovio support. It is not your gateway password, and it must never be shipped
to a browser.

## What is next

- [How the SDKs think](https://developer.inoviopay.com/sdks/concepts.md) walks the shared object model: the status
  lifecycle, decimal money, idempotency, outcome tiers, order-level
  reconciliation, tokenization and 3D Secure.
- The per-language pages give the full method surface, the language-specific
  ergonomics, and the runnable examples in each repository.
- [Shopping carts](https://developer.inoviopay.com/carts/index.md) covers the pre-built store plugins, which
  are a different integration path from calling an SDK yourself.
