A complete charge-and-refund implementation using the Global Payments GP API. Developers can charge a card using hosted tokenization, then issue a full or partial refund against any transaction ID — all without handling raw card data. All implementations use the official Global Payments SDK (GpApiConfig).
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):
This tool demonstrates a two-step payment lifecycle:
- Charge — The frontend loads a hosted payment form from GP API. The customer enters card details, which are tokenized client-side. The token is sent to
POST /charge, which calls the GP API SDK to capture the payment and returns atransactionId. - Refund — The
transactionIdfrom the charge is passed toPOST /refundalong with the refund amount. The backend callsTransaction.fromId()on the SDK to issue the refund without needing the original card data.
Browser
│
├─ GET /config ──────────────────► Server
│ └─ GP API: generate access token
│ ◄── { accessToken, environment } ──┘
│
├─ Hosted fields tokenize card (client-side, PCI-compliant)
│
├─ POST /charge ─────────────────► Server
│ { payment_token, amount } └─ SDK: CreditCardData.charge().execute()
│ ◄── { transactionId, status } ───┘
│
└─ POST /refund ─────────────────► Server
{ transactionId, amount } └─ SDK: Transaction.fromId().refund().execute()
◄── { refundId, status } ────────┘
- Global Payments developer account — Sign up at developer.globalpayments.com
- GP API credentials:
APP_IDandAPP_KEY(sandbox credentials available after sign-up) - 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/basic-refund-tool.git
cd basic-refund-toolcd php # or nodejs, dotnet, java
cp .env.sample .envEdit .env:
GP_API_APP_ID=your_app_id_here
GP_API_APP_KEY=your_app_key_here
GP_API_ENVIRONMENT=testPHP:
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 an amount (e.g.
19.99) - Use a test card (see Test Cards below)
- Click Charge — note the
transactionIdin the response - Enter that
transactionIdand an amount to refund - Click Refund — confirm the refund response
Run all four language implementations simultaneously:
# Copy root-level env (used by all services)
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 dotnet # http://localhost:8006
docker-compose up java # http://localhost:8004Run integration tests (requires all services healthy):
docker-compose --profile testing upTest results written to ./test-results/ and ./playwright-report/.
Returns a GP API access token for client-side hosted field initialization. The token has restricted permissions (PMT_POST_Create_Single) — it can only tokenize cards, not process transactions.
Response:
{
"success": true,
"data": {
"accessToken": "uua7....",
"environment": "test",
"supportedCurrencies": ["USD", "EUR", "GBP", "CAD"],
"defaultCurrency": "USD",
"refund": {
"maxPercentage": 115,
"timeWindowDays": 180
}
}
}Processes a card payment using a tokenized payment reference.
Request body:
{
"payment_token": "PMT_abc123...",
"amount": 19.99,
"currency": "USD",
"billing_zip": "10001",
"cardDetails": {
"cardType": "visa",
"cardLast4": "9299"
}
}Success response (200):
{
"success": true,
"message": "Payment processed successfully",
"data": {
"transactionId": "TRN_abc123xyz",
"amount": 19.99,
"currency": "USD",
"status": "captured",
"responseCode": "SUCCESS",
"responseMessage": "Approved",
"authorizationCode": "12345",
"referenceNumber": "REF_001",
"timestamp": "2025-01-15T10:30:00.000Z",
"paymentMethod": {
"type": "card",
"brand": "Visa",
"last4": "9299"
}
}
}Error response (422):
{
"success": false,
"message": "Payment failed: Declined",
"error_code": "PAYMENT_DECLINED",
"timestamp": "2025-01-15T10:30:00.000Z"
}Issues a refund against an existing transaction ID. Supports partial refunds (amount less than original) and full refunds.
Request body:
{
"transactionId": "TRN_abc123xyz",
"amount": 19.99,
"currency": "USD",
"reason": "Customer requested refund"
}Success response (200):
{
"success": true,
"message": "Refund processed successfully",
"data": {
"refundId": "TRN_ref456xyz",
"transactionId": "TRN_abc123xyz",
"amount": 19.99,
"currency": "USD",
"status": "captured",
"responseCode": "SUCCESS",
"responseMessage": "Refunded",
"authorizationCode": "67890",
"referenceNumber": "REF_002",
"reason": "Customer requested refund",
"timestamp": "2025-01-15T10:35:00.000Z"
}
}Error response (422):
{
"success": false,
"message": "Refund failed: Transaction not found",
"error_code": "REFUND_DECLINED",
"timestamp": "2025-01-15T10:35:00.000Z"
}Use these in sandbox (GP_API_ENVIRONMENT=test). CVV: 123. Expiry: any future date.
| Brand | Card Number | Expected Result |
|---|---|---|
| Visa | 4263 9826 4026 9299 | Approved |
| Visa | 4263 9700 0000 5262 | Approved |
| Mastercard | 5425 2334 2424 1200 | Approved |
| Discover | 6011 0000 0000 0012 | Approved |
| Amex | 3714 496353 98431 | Approved |
Sandbox transactions do not move real money. Use test credentials only.
basic-refund-tool/
├── 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
│ ├── PaymentUtils.php # SDK config + shared helpers
│ ├── config.php # GET /config endpoint
│ ├── charge.php # POST /charge endpoint
│ ├── refund.php # POST /refund endpoint
│ └── index.html # Serves frontend
│
├── nodejs/ # Port 8001
│ ├── .env.sample
│ ├── package.json
│ ├── Dockerfile
│ └── server.js # Express app: /config, /charge, /refund
│
├── dotnet/ # Port 8006
│ ├── .env.sample
│ ├── dotnet.csproj
│ ├── Program.cs # ASP.NET Core app: all endpoints
│ ├── Dockerfile
│ └── wwwroot/ # Static frontend files
│
└── java/ # Port 8004
├── .env.sample
├── pom.xml
├── Dockerfile
└── src/
└── main/java/com/globalpayments/example/
├── ConfigServlet.java
├── ProcessPaymentServlet.java
└── RefundServlet.java
All language implementations use the same three variables:
| Variable | Description | Example |
|---|---|---|
GP_API_APP_ID |
Your GP API application ID | UJqPrAhrDkGzzNoFInpzKqoI8vfZtGRV |
GP_API_APP_KEY |
Your GP API application key | zCFrbrn0NKly9sB4 |
GP_API_ENVIRONMENT |
test for sandbox, production for live |
test |
Credentials are available in the GP Developer Portal after creating an account.
401 Unauthorized on /config
Credentials are invalid or for the wrong environment. Verify GP_API_APP_ID and GP_API_APP_KEY in .env match the environment setting (test vs production).
422 on /charge — "Payment failed"
The test card may have been declined. Try a different card from the Test Cards table. Confirm GP_API_ENVIRONMENT=test when using test cards.
422 on /refund — "Transaction not found"
The transactionId does not exist in sandbox or has expired. Use the transactionId returned by a /charge call made in the same session with the same credentials.
Port already in use
Another process is using the port. Either stop it (lsof -i :8003) or change the port mapping in docker-compose.yml.
Java build fails — mvn cargo:run
Requires Java 17+ and Maven 3.8+. Verify with java -version and mvn -version.
.NET — dotenv.net missing
Run dotnet restore before dotnet run to install NuGet dependencies.
- 🌐 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 communityexperience@globalpay.com.
MIT — see LICENSE.