GP API-based card payment processing with multi-currency and multi-language support using Global Payments Java SDK.
- Client-Side Tokenization: Secure card data capture using GP API JavaScript SDK
- Server-Side Payment Processing: Token-based payment charging via GP API
- Multi-Currency Support: Process payments in USD, EUR, GBP, CAD, AUD, JPY
- Multi-Language Support: Interface available in English, Spanish, French, German, Portuguese
- Independent Selection: Currency and language can be chosen independently
- Automatic Locale Detection: Browser language detection with manual override
- Currency Formatting: Locale-aware number and currency formatting using Intl API
- Session Persistence: User preferences saved across sessions using HttpSession
- Card Details Management: Automatic extraction and return of card metadata
- CORS Support: Cross-origin request handling for frontend integration
- Error Handling: Comprehensive exception handling with localized error messages
- Jakarta EE Servlets: Modern servlet-based architecture with annotations
- Java 11 or later (tested with Java 23)
- Maven 3.6 or later
- Global Payments account and GP API credentials
- Dependencies (auto-installed via Maven):
- globalpayments-sdk (14.2.20)
- jakarta.servlet-api (5.0.0)
- dotenv-java (3.0.0)
- gson (2.10.1)
src/main/java/com/globalpayments/example/ProcessPaymentServlet.java- Main servlet with API endpoints and SDK configurationsrc/main/java/com/globalpayments/example/LocaleServlet.java- Locale preference management servletsrc/main/webapp/index.html- Frontend payment form with localization featuressrc/main/webapp/WEB-INF/web.xml- Web application configuration.env.sample- Environment configuration templatepom.xml- Maven dependencies and build configurationrun.sh- Convenience script to run the application
src/main/java/com/globalpayments/example/services/LocaleService.java- Locale detection, validation, and session managementsrc/main/java/com/globalpayments/example/services/CurrencyConfig.java- Currency metadata and formatting rulessrc/main/java/com/globalpayments/example/services/TranslationService.java- Server-side translation handling
src/main/resources/translations/en.json- English translationssrc/main/resources/translations/es.json- Spanish translationssrc/main/resources/translations/fr.json- French translationssrc/main/resources/translations/de.json- German translationssrc/main/resources/translations/pt.json- Portuguese translations
src/main/webapp/js/translations.js- Client-side i18n modulesrc/main/webapp/js/currency-formatter.js- Currency formatting utilities
-
Clone or navigate to this directory
-
Copy
.env.sampleto.env:cp .env.sample .env
-
Update
.envwith your GP API credentials:GP_API_APP_ID=your_gp_api_app_id_here GP_API_APP_KEY=your_gp_api_app_key_here GP_API_ENVIRONMENT=sandbox
Get your credentials from: https://developer.globalpayments.com/
-
Install dependencies:
mvn clean install
-
Run the application:
./run.sh
Or manually:
mvn cargo:run
-
Open in browser:
http://localhost:8000
Generates GP API access token with locale/currency configuration for client-side SDK initialization.
Headers Required:
- Accept-Language: (optional) Browser language for locale detection
Response:
{
"success": true,
"data": {
"accessToken": "PMT_...",
"locale": "en",
"currency": "USD",
"supportedLocales": {
"en": {"code": "en", "name": "English", "nativeName": "English", "defaultCurrency": "USD"},
"es": {"code": "es", "name": "Spanish", "nativeName": "Español", "defaultCurrency": "EUR"},
"fr": {"code": "fr", "name": "French", "nativeName": "Français", "defaultCurrency": "EUR"},
"de": {"code": "de", "name": "German", "nativeName": "Deutsch", "defaultCurrency": "EUR"},
"pt": {"code": "pt", "name": "Portuguese", "nativeName": "Português", "defaultCurrency": "EUR"}
},
"supportedCurrencies": {
"USD": {"code": "USD", "symbol": "$", "decimals": 2, "country": "US"},
"EUR": {"code": "EUR", "symbol": "€", "decimals": 2, "country": "GB"},
"GBP": {"code": "GBP", "symbol": "£", "decimals": 2, "country": "GB"},
"CAD": {"code": "CAD", "symbol": "C$", "decimals": 2, "country": "CA"},
"AUD": {"code": "AUD", "symbol": "A$", "decimals": 2, "country": "AU"},
"JPY": {"code": "JPY", "symbol": "¥", "decimals": 0, "country": "JP"}
}
},
"message": "Configuration retrieved successfully",
"timestamp": "2025-11-25T..."
}Retrieves current user's locale and currency settings with translations.
Response:
{
"success": true,
"data": {
"locale": "en",
"currency": "USD",
"translations": {
"form.amount": "Amount",
"button.process_payment": "Process Payment",
"message.success": "Payment Successful!"
},
"supportedLocales": {...},
"supportedCurrencies": {...}
},
"timestamp": "2025-11-25T..."
}Updates user locale and currency preferences.
Request:
{
"locale": "es",
"currency": "EUR"
}Response:
{
"success": true,
"data": {
"locale": "es",
"currency": "EUR",
"translations": {...}
},
"message": "Locale preferences updated",
"timestamp": "2025-11-25T..."
}Processes a payment with the provided token and localized preferences.
Request:
{
"payment_token": "PMT_xxx",
"amount": 25.00,
"currency": "EUR",
"locale": "es"
}Response (Success):
{
"success": true,
"data": {
"transactionId": "txn_xxx",
"amount": 25.00,
"currency": "EUR",
"status": "CAPTURED",
"reference": "ref_xxx",
"timestamp": "2025-11-25T..."
},
"message": "¡Pago Exitoso!",
"timestamp": "2025-11-25T..."
}Response (Error):
{
"success": false,
"message": "Pago fallido: Fondos insuficientes",
"error_code": "API_ERROR",
"timestamp": "2025-11-25T..."
}- en (English) - Default currency: USD
- es (Spanish/Español) - Default currency: EUR
- fr (French/Français) - Default currency: EUR
- de (German/Deutsch) - Default currency: EUR
- pt (Portuguese/Português) - Default currency: EUR
- USD (US Dollar) - Symbol: $, Decimals: 2, Country: US
- EUR (Euro) - Symbol: €, Decimals: 2, Country: GB
- GBP (British Pound) - Symbol: £, Decimals: 2, Country: GB
- CAD (Canadian Dollar) - Symbol: C$, Decimals: 2, Country: CA
- AUD (Australian Dollar) - Symbol: A$, Decimals: 2, Country: AU
- JPY (Japanese Yen) - Symbol: ¥, Decimals: 0, Country: JP
- Session storage (if user previously selected)
- Accept-Language HTTP header (browser preference)
- Default: English (en)
- Session storage (if user previously selected)
- Default currency for detected locale
- Default: USD
Browser → POST /config → Server generates GP API token
→ Detects locale from Accept-Language header
→ Returns token + locale/currency + translations
Frontend → Initializes GP API SDK with token
→ Sets UI language based on locale
→ Formats currency based on currency code
User selects language → POST /api/locale with new preferences
→ Server updates HttpSession
→ Returns new translations
Frontend → Updates UI with new language
User enters card → GP API SDK tokenizes (client-side)
→ Returns payment_token
Frontend → POST /process-payment with token + amount + currency + locale
Server → Reconfigures SDK with dynamic country code based on currency
→ Processes payment
→ Returns localized success/error message
This implementation uses Jakarta EE HttpSession to persist user preferences:
Configuration (in ProcessPaymentServlet.java):
HttpSession session = request.getSession(true);
session.setAttribute("locale", locale);
session.setAttribute("currency", currency);Session Data Stored:
locale- User's selected language codecurrency- User's selected currency code
Persistence:
- Session timeout configured in
web.xml(default: 30 minutes) - Memory-based session storage (servlet container default)
- Session survives browser restart (via JSESSIONID cookie)
Production Considerations:
- Configure persistent session store (Redis, database) in servlet container
- Set appropriate session timeout in
web.xml:<session-config> <session-timeout>1440</session-timeout> <!-- 24 hours --> </session-config>
- Configure secure cookies in production
- Use session clustering for multi-server deployments
Java implementation uses the SDK's generateTransactionKey() method for secure token generation:
// Configure GP API for token generation
GpApiConfig config = new GpApiConfig();
config.setAppId(dotenv.get("GP_API_APP_ID"));
config.setAppKey(dotenv.get("GP_API_APP_KEY"));
config.setEnvironment(Environment.TEST);
config.setChannel(Channel.CardNotPresent);
config.setCountry(countryCode);
config.setPermissions(new String[]{"PMT_POST_Create_Single"});
config.setSecondsToExpire(600);
// Generate access token using SDK
var accessTokenInfo = GpApiService.generateTransactionKey(config);
String accessToken = accessTokenInfo.getAccessToken();Benefits of SDK Approach:
- Consistent behavior with other SDK implementations
- Proper error handling built into SDK
- Automatic nonce and secret generation
- Matches PHP, .NET, and Node.js implementations
This implementation uses Jakarta EE servlet annotations:
@WebServlet(urlPatterns = {"/process-payment", "/config"})
public class ProcessPaymentServlet extends HttpServlet {
// Servlet handles multiple endpoints
}Benefits:
- No explicit
web.xmlservlet mapping needed - Clean annotation-based configuration
- Modern Jakarta EE approach
- Easy to add new endpoints
- User opens page → Detects English browser → Shows English UI with USD currency
- Enters test card
4263 9826 4026 9299 - Clicks "Process Payment"
- Receives: "Payment Successful! Transaction ID: txn_xxx"
- User selects "Español" from language dropdown
- UI updates to Spanish
- User selects "EUR" from currency dropdown
- Amount shows as "25,00 €" (European formatting)
- Enters test card
- Clicks "Procesar Pago"
- Receives: "¡Pago Exitoso! ID de transacción: txn_xxx"
- User selects "English" language
- User selects "JPY" currency
- Amount shows as "¥2,500" (no decimals for JPY)
- Processes payment
- GP API routes to Japan (JP country code)
-
Create translation file:
cp src/main/resources/translations/en.json src/main/resources/translations/it.json
-
Translate all keys in
it.json:{ "form.amount": "Importo", "button.process_payment": "Elabora Pagamento", "message.success": "Pagamento Riuscito!" } -
Update LocaleService.java - Add to supported locales:
private static final Map<String, LocaleInfo> SUPPORTED_LOCALES = Map.of( // ... existing locales "it", new LocaleInfo("it", "Italian", "Italiano", "EUR") );
-
Update frontend
src/main/webapp/js/translations.js:const translations = { // ... existing it: { /* Italian translations */ } };
-
Update HTML language selector in
src/main/webapp/index.html:<option value="it">🇮🇹 Italiano</option>
-
Update CurrencyConfig.java:
private static final Map<String, CurrencyInfo> SUPPORTED_CURRENCIES = Map.of( // ... existing "CHF", new CurrencyInfo("CHF", "CHF", 2, "CH") );
-
Update frontend
src/main/webapp/js/currency-formatter.js:const currencies = { // ... existing CHF: { code: 'CHF', symbol: 'CHF', decimals: 2, country: 'CH' } };
-
Update HTML currency selector:
<option value="CHF">🇨🇭 CHF - Swiss Franc (CHF)</option>
-
Verify GP API support for the country code
Use these test cards for different scenarios:
Visa - Successful:
- Card Number:
4263 9826 4026 9299 - Expiry: Any future date (e.g.,
12/25) - CVV: Any 3 digits (e.g.,
123)
Visa - Declined (Insufficient Funds):
- Card Number:
4000120000001154
Mastercard - Successful:
- Card Number:
5425 2334 2424 1200
- Change browser language to Spanish (es)
- Reload page
- Verify UI is in Spanish
- Verify default currency is EUR
- Select language: French, currency: CAD
- Reload page (F5)
- Verify French + CAD are still selected
- Close tab, reopen within session timeout
- Verify preferences persisted
- USD:
$25.00(before, period decimal) - EUR:
25,00 €(after, comma decimal) - JPY:
¥2500(no decimals)
# Get config
curl -X POST http://localhost:8000/config \
-H "Accept-Language: es-ES,es;q=0.9"
# Process payment
curl -X POST http://localhost:8000/process-payment \
-H "Content-Type: application/json" \
-d '{
"payment_token": "PMT_xxx",
"amount": 25.00,
"currency": "EUR",
"locale": "es"
}'Cause: Invalid GP API credentials
Solution:
- Verify
.envfile exists and has correct variables:GP_API_APP_ID=your_actual_app_id GP_API_APP_KEY=your_actual_app_key
- Verify credentials are for GP API (not Portico/Heartland)
- Check credentials at https://developer.globalpayments.com/
Cause: Credentials not recognized by GP API
Solution:
- Ensure you're using GP API credentials (not Heartland/Portico)
- Verify APP_ID and APP_KEY are correct
- Check environment is set to
sandboxfor test credentials
Cause: Session configuration issue or timeout
Solution:
- Verify session timeout in
web.xml:<session-config> <session-timeout>30</session-timeout> </session-config>
- Check browser cookies are enabled
- Clear browser cache and cookies
- Verify JSESSIONID cookie is being set
Cause: Browser doesn't support Intl.NumberFormat or incorrect locale
Solution:
- Update browser to latest version
- Check browser console for Intl errors
- Verify locale code is correct (e.g.,
en, noten-US)
Cause: Translation file missing or malformed JSON
Solution:
- Verify translation file exists:
src/main/resources/translations/{locale}.json - Validate JSON syntax (use JSON validator)
- Check browser console for JSON parsing errors
- Ensure translation keys match frontend expectations
Cause: Dependency issues or Java version mismatch
Solution:
- Clean and rebuild:
mvn clean install -U
- Verify Java version:
java -version # Should be 11 or later - Check Maven version:
mvn -version # Should be 3.6 or later
Cause: Another application using port 8000
Solution:
- Stop other application using port 8000
- Or change port in
pom.xml:<cargo.servlet.port>8080</cargo.servlet.port>
This is a demonstration implementation. For production use, add:
- Input Validation: Validate all user inputs (amount, currency codes, locale codes)
- Rate Limiting: Prevent brute force attacks on payment endpoint
- CSRF Protection: Implement CSRF tokens for state-changing operations
- Security Headers: Add security headers via servlet filters
response.setHeader("X-Frame-Options", "DENY"); response.setHeader("X-Content-Type-Options", "nosniff"); response.setHeader("Content-Security-Policy", "default-src 'self'");
- Logging: Implement comprehensive request/response logging with SLF4J/Logback
- PCI Compliance: Ensure no card data touches your server (token-based only)
- HTTPS: Use TLS in production (required for payment processing)
- Environment Variables: Use secure secrets management (AWS Secrets Manager, Azure Key Vault)
- Session Security: Configure secure, HTTP-only cookies in production
- CORS: Configure CORS properly for production domains
- Global Payments Developer Portal
- GP API Documentation
- Java SDK Documentation
- GP API Reference
- Jakarta EE Documentation
- Apache Maven Documentation
MIT License - See LICENSE file for details