GP API-based card payment processing with multi-currency and multi-language support using Global Payments .NET 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 ASP.NET Core session middleware
- 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
- Minimal API: Modern ASP.NET Core minimal API approach for lightweight endpoints
- .NET 9.0 or later
- Global Payments account and GP API credentials
- NuGet packages (auto-restored):
- GlobalPayments.Api (v9.0.16)
- dotenv.net
- System.Text.Json
Program.cs- Main application with minimal API endpoints and SDK configurationwwwroot/index.html- Frontend payment form with localization features.env.sample- Environment configuration templaterun.sh- Convenience script to run the applicationappsettings.json- Application configuration file
Services/LocaleService.cs- Locale detection, validation, and session managementServices/CurrencyConfig.cs- Currency metadata and formatting rulesServices/TranslationService.cs- Server-side translation handling
translations/en.json- English translationstranslations/es.json- Spanish translationstranslations/fr.json- French translationstranslations/de.json- German translationstranslations/pt.json- Portuguese translations
wwwroot/js/translations.js- Client-side i18n modulewwwroot/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/
-
Restore dependencies:
dotnet restore
-
Run the application:
./run.sh
Or manually:
dotnet 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 → /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 session
→ 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 → Configures SDK with dynamic country code based on currency
→ Processes payment
→ Returns localized success/error message
This implementation uses ASP.NET Core session middleware to persist user preferences:
Configuration (in Program.cs):
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(24);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});Session Data Stored:
locale- User's selected language codecurrency- User's selected currency code
Persistence:
- 24-hour timeout
- HTTP-only cookies for security
- Essential cookie (GDPR-compliant)
.NET implementation uses the SDK's GenerateTransactionKey() method for secure token generation:
// Configure GP API for token generation
var config = new GpApiConfig
{
AppId = System.Environment.GetEnvironmentVariable("GP_API_APP_ID"),
AppKey = System.Environment.GetEnvironmentVariable("GP_API_APP_KEY"),
Environment = GlobalPayments.Api.Entities.Environment.TEST,
Channel = Channel.CardNotPresent,
Country = countryCode,
Permissions = new[] { "PMT_POST_Create_Single" }
};
// Configure service first
ServicesContainer.ConfigureService(config);
// Generate access token using SDK
var accessTokenInfo = GpApiService.GenerateTransactionKey(config);
var accessToken = accessTokenInfo.Token;Benefits of SDK Approach:
- Consistent behavior with other SDK implementations
- Proper error handling built into SDK
- Automatic nonce and secret generation
- Matches PHP, Java, and Node.js implementations
- 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 translations/en.json translations/it.json
-
Translate all keys in
translations/it.json:{ "form.amount": "Importo", "button.process_payment": "Elabora Pagamento", "message.success": "Pagamento Riuscito!" } -
Update LocaleService.cs - Add to supported locales:
private static readonly Dictionary<string, LocaleInfo> SupportedLocales = new() { // ... existing locales { "it", new LocaleInfo { Code = "it", Name = "Italian", NativeName = "Italiano", DefaultCurrency = "EUR" } } };
-
Update frontend
wwwroot/js/translations.js:const translations = { // ... existing it: { /* Italian translations */ } };
-
Update HTML language selector in
wwwroot/index.html:<option value="it">🇮🇹 Italiano</option>
-
Update CurrencyConfig.cs:
private static readonly Dictionary<string, CurrencyInfo> SupportedCurrencies = new() { // ... existing { "CHF", new CurrencyInfo { Code = "CHF", Symbol = "CHF", Decimals = 2, Country = "CH", // ... other properties }} };
-
Update frontend
wwwroot/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 24 hours
- 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: Cookies disabled or session configuration issue
Solution:
- Enable cookies in browser
- Verify session middleware is configured in
Program.cs - Check browser console for cookie errors
- Clear browser cache and cookies
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:
translations/{locale}.json - Validate JSON syntax (use JSON validator)
- Check browser console for JSON parsing errors
- Ensure translation keys match frontend expectations
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 anti-CSRF tokens for state-changing operations
- Security Headers: Add Content-Security-Policy, X-Frame-Options, etc.
- Logging: Implement comprehensive request/response logging
- 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 (Azure Key Vault, AWS Secrets Manager)
MIT License - See LICENSE file for details