A complete ACH/eCheck payment implementation using the Global Payments Portico gateway. Developers can process electronic check payments using direct bank account information — account number, routing number, and account type — without card tokenization. All implementations use the official Global Payments SDK (PorticoConfig).
Available in four languages: PHP, Node.js, .NET, and Java.
| Language | Framework | SDK Version |
|---|---|---|
| PHP | Built-in Server | globalpayments/php-sdk ^13.1 |
| Node.js | Express.js | globalpayments-api ^3.10.6 |
| .NET | ASP.NET Core | GlobalPayments.Api 9.0.16 |
| Java | Jakarta Servlet | globalpayments-sdk 14.2.20 |
Preview links (runs in browser via CodeSandbox):
ACH/eCheck payments are processed entirely server-side. Unlike card payments, there is no client-side tokenization step — the customer enters bank account details directly into the form, and the backend constructs an ECheck object and submits it to Portico.
Browser
│
├─ GET /config ──────────────────► Server
│ └─ Returns { directEntry: true }
│ (no public key needed)
│
├─ Customer enters bank details
│ account_number, routing_number,
│ account_type, check_type, name
│
└─ POST /process-payment ────────► Server
{ account_number, routing_number, └─ Validates routing checksum
account-type, check-type, └─ Builds ECheck object
check_holder_name, amount } └─ SDK: ECheck.charge().execute()
◄── { transactionId, status } ─────┘ via PorticoConfig
All implementations validate routing numbers using the standard ABA checksum algorithm before sending to the gateway. Invalid routing numbers are rejected client-side with a clear error before any API call is made.
- Global Payments developer account with Portico credentials — Sign up at developer.globalpayments.com
- Two API keys from your Portico account:
PUBLIC_API_KEY— prefixedpkapi_cert_...(sandbox) orpkapi_prod_...(production)SECRET_API_KEY— prefixedskapi_cert_...(sandbox) orskapi_prod_...(production)
- Docker (for multi-service setup), or a local runtime for your chosen language:
- PHP 8.0+ with Composer
- Node.js 18+ with npm
- .NET 8.0 SDK
- Java 17+ with Maven
git clone https://github.com/globalpayments-samples/portico-online-check-payments.git
cd portico-online-check-paymentscd php # or nodejs, dotnet, java
cp .env.sample .envEdit .env:
PUBLIC_API_KEY=pkapi_cert_your_key_here
SECRET_API_KEY=skapi_cert_your_key_herePHP:
composer install
php -S localhost:8003Open: http://localhost:8003
Node.js:
npm install
npm startOpen: http://localhost:8001
.NET:
dotnet restore
dotnet runOpen: http://localhost:8006
Java:
mvn clean package
mvn cargo:runOpen: http://localhost:8004
- Open the app in your browser
- Enter a test bank account (see Test Accounts below)
- Fill in routing number, account type, check type, and amount
- Click Submit — confirm the
transactionIdin the response
Run all four implementations simultaneously:
cp .env.sample .env
# Edit .env with your credentials, then:
docker-compose upIndividual services:
docker-compose up nodejs # http://localhost:8001
docker-compose up php # http://localhost:8003
docker-compose up java # http://localhost:8004
docker-compose up dotnet # http://localhost:8006Run integration tests:
docker-compose --profile testing upReturns minimal configuration for the payment form. No public key is returned because ACH/eCheck uses direct entry — there is no client-side tokenization.
Response:
{
"success": true,
"data": {
"directEntry": true,
"message": "Direct bank account entry enabled"
}
}Processes an ACH/eCheck payment using direct bank account data.
Request body:
{
"account_number": "12345678901",
"routing_number": "122105155",
"account-type": "checking",
"check-type": "personal",
"check_holder_name": "Jane Smith",
"amount": "25.00",
"billing_zip": "30303"
}| Field | Required | Values |
|---|---|---|
account_number |
Yes | Bank account number (digits only) |
routing_number |
Yes | 9-digit ABA routing number |
account-type |
Yes | checking or savings |
check-type |
Yes | personal or business |
check_holder_name |
Yes | Name on the bank account |
amount |
Yes | Positive decimal (e.g. 25.00) |
billing_zip |
No | Postal code for AVS |
Success response (200):
{
"success": true,
"message": "Payment successful! Transaction ID: 1234567890",
"data": {
"transactionId": "1234567890",
"responseCode": "00",
"responseMessage": "Transaction Approved"
}
}Validation error (400):
{
"success": false,
"message": "Payment processing failed",
"error": {
"code": "PAYMENT_DECLINED",
"details": "Invalid routing number"
}
}Use these in sandbox environments. Portico sandbox does not validate real account numbers — use any plausible format.
| Field | Test Value |
|---|---|
| Account Number | 12345678901 |
| Routing Number | 122105155 (valid ABA checksum) |
| Account Type | checking |
| Check Type | personal |
| Amount | Any positive decimal |
Sandbox transactions do not move real money. Use sandbox credentials only.
portico-online-check-payments/
├── index.html # Shared frontend (served by all backends)
├── docker-compose.yml # Multi-service Docker config
├── Dockerfile.tests # Playwright test runner
├── LICENSE
├── README.md
│
├── php/ # Port 8003
│ ├── .env.sample
│ ├── composer.json
│ ├── Dockerfile
│ ├── config.php # GET /config endpoint
│ ├── process-payment.php # POST /process-payment endpoint
│ └── index.html
│
├── nodejs/ # Port 8001
│ ├── .env.sample
│ ├── package.json
│ ├── Dockerfile
│ └── server.js # Express app: /config, /process-payment
│
├── dotnet/ # Port 8006
│ ├── .env.sample
│ ├── dotnet.csproj
│ ├── Program.cs
│ ├── Dockerfile
│ └── wwwroot/
│
└── java/ # Port 8004
├── .env.sample
├── pom.xml
├── Dockerfile
└── src/
└── main/java/com/globalpayments/example/
| Variable | Description | Example |
|---|---|---|
PUBLIC_API_KEY |
Portico public key for reference | pkapi_cert_jKc1FtuyAydZhZfbB3 |
SECRET_API_KEY |
Portico secret key for server-side SDK auth | skapi_cert_MTyM... |
Note: For ACH/eCheck, only
SECRET_API_KEYis used for transaction processing.PUBLIC_API_KEYis present for consistency with other Portico projects.
Invalid routing number error
The routing number failed the ABA checksum validation. Use a valid 9-digit routing number — 122105155 works for sandbox testing.
Transaction Declined response
Portico sandbox may decline transactions with certain account/routing combinations. Use the test values in Test Accounts.
Port already in use
Stop the conflicting process (lsof -i :8001) or change the port mapping in docker-compose.yml.
PHP — composer: command not found
Install Composer: curl -sS https://getcomposer.org/installer | php && mv composer.phar /usr/local/bin/composer
Java build fails
Requires Java 17+ and Maven 3.8+. Verify with java -version and mvn -version.
.NET — missing packages
Run dotnet restore before dotnet run.
- 🌐 Developer Portal — developer.globalpayments.com
- 💬 Discord — Join the community
- 📋 GitHub Discussions — github.com/orgs/globalpayments/discussions
- 📧 Newsletter — Subscribe
- 💼 LinkedIn — Global Payments for Developers
Have a question or found a bug? Open an issue or reach out at [email protected].
MIT — see LICENSE.