{"id":50946105,"url":"https://github.com/globalpayments-samples/online-recurring-payments","last_synced_at":"2026-06-17T20:07:35.925Z","repository":{"id":349007112,"uuid":"1073940742","full_name":"globalpayments-samples/online-recurring-payments","owner":"globalpayments-samples","description":"Recurring payment and subscription billing examples with Global Payments SDK. Set up automated payment schedules with customer vault integration.","archived":false,"fork":false,"pushed_at":"2026-05-22T13:49:22.000Z","size":150,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T18:58:32.942Z","etag":null,"topics":["automated-payments","billing","customer-vault","global-payments","lang-dotnet","lang-go","lang-java","lang-nodejs","lang-php","lang-python","payment-schedule","recurring-payments","saas","sdk","subscriptions"],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/globalpayments-samples.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-10-10T21:35:35.000Z","updated_at":"2026-05-22T13:49:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/globalpayments-samples/online-recurring-payments","commit_stats":null,"previous_names":["globalpayments-samples/online-recurring-payments"],"tags_count":0,"template":false,"template_full_name":"globalpayments-samples/starter-template","purl":"pkg:github/globalpayments-samples/online-recurring-payments","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalpayments-samples%2Fonline-recurring-payments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalpayments-samples%2Fonline-recurring-payments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalpayments-samples%2Fonline-recurring-payments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalpayments-samples%2Fonline-recurring-payments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/globalpayments-samples","download_url":"https://codeload.github.com/globalpayments-samples/online-recurring-payments/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globalpayments-samples%2Fonline-recurring-payments/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34463588,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["automated-payments","billing","customer-vault","global-payments","lang-dotnet","lang-go","lang-java","lang-nodejs","lang-php","lang-python","payment-schedule","recurring-payments","saas","sdk","subscriptions"],"created_at":"2026-06-17T20:07:35.083Z","updated_at":"2026-06-17T20:07:35.911Z","avatar_url":"https://github.com/globalpayments-samples.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Online Recurring Payments — GP API\n\nA complete recurring payment implementation using the Global Payments GP API. Developers can set up subscription billing by capturing an initial payment with customer data, tokenizing the card server-side with `StoredCredential`, and scheduling future charges against the stored payment method — without handling raw card numbers at any point. All implementations use the official Global Payments SDK (`GpApiConfig`).\n\nAvailable in four languages: PHP, Node.js, .NET, and Java.\n\n---\n\n## Available Implementations\n\n| Language | Framework | SDK Version |\n|----------|-----------|-------------|\n| [**PHP**](./php/) | Built-in Server | globalpayments/php-sdk ^13.1 |\n| [**Node.js**](./nodejs/) | Express.js | globalpayments-api ^3.10.6 |\n| [**.NET**](./dotnet/) | ASP.NET Core | GlobalPayments.Api 9.0.16 |\n| [**Java**](./java/) | Jakarta Servlet | globalpayments-sdk 14.2.20 |\n\nPreview links (runs in browser via CodeSandbox):\n- [PHP Preview](https://githubbox.com/globalpayments-samples/online-recurring-payments/tree/main/php)\n- [Node.js Preview](https://githubbox.com/globalpayments-samples/online-recurring-payments/tree/main/nodejs)\n- [.NET Preview](https://githubbox.com/globalpayments-samples/online-recurring-payments/tree/main/dotnet)\n- [Java Preview](https://githubbox.com/globalpayments-samples/online-recurring-payments/tree/main/java)\n\n---\n\n## How It Works\n\nThis project demonstrates the full recurring payment lifecycle: an initial charge captures the customer's card and stores it as a reusable payment method. All future charges use the stored method without prompting the customer for card details again.\n\n```\nBrowser\n  │\n  ├─ GET /config ──────────────────► Server\n  │                                    └─ GP API: generate scoped access token\n  │  ◄── { accessToken } ──────────────┘\n  │\n  ├─ Hosted fields tokenize card (client-side, PCI-compliant)\n  │\n  ├─ POST /process-payment ────────► Server\n  │   {                               ├─ is_recurring: false → charge().execute()\n  │     payment_token,                └─ is_recurring: true  → charge()\n  │     amount,                                                 .withStoredCredential()\n  │     is_recurring,                                           .withCustomer()\n  │     frequency,                                              .withAddress()\n  │     start_date,                                             .execute()\n  │     first_name, last_name,\n  │     email, phone, address         Returns transaction_id + payment_method_id\n  │   }\n  │  ◄── { transactionId, paymentMethodId, customerId, schedule } ──┘\n  │\n  └─ Future charges use paymentMethodId (no card re-entry)\n```\n\n### StoredCredential Flow\n\nThe initial charge uses `StoredCredentialType.INSTALLMENT` and `StoredCredentialSequence.FIRST`. All subsequent charges reference the same `paymentMethodId` with `StoredCredentialSequence.SUBSEQUENT`. This meets card network requirements for recurring billing and avoids false fraud declines.\n\n---\n\n## Prerequisites\n\n- Global Payments developer account — [Sign up at developer.globalpayments.com](https://developer.globalpayments.com)\n- GP API credentials: `APP_ID` and `APP_KEY` (sandbox available after sign-up)\n- A local runtime for your chosen language:\n  - PHP 8.0+ with Composer\n  - Node.js 18+ with npm\n  - .NET 8.0 SDK\n  - Java 17+ with Maven\n\n---\n\n## Quick Start\n\n### 1. Clone the repository\n\n```bash\ngit clone https://github.com/globalpayments-samples/online-recurring-payments.git\ncd online-recurring-payments\n```\n\n### 2. Choose a language and configure credentials\n\n```bash\ncd php       # or nodejs, dotnet, java\ncp .env.sample .env\n```\n\nEdit `.env`:\n\n```env\nAPP_ID=your_gp_api_app_id_here\nAPP_KEY=your_gp_api_app_key_here\nGP_API_ENVIRONMENT=sandbox\n```\n\n### 3. Install and run\n\n**PHP:**\n```bash\ncomposer install\nphp -S localhost:8000\n```\nOpen: http://localhost:8000\n\n**Node.js:**\n```bash\nnpm install\nnpm start\n```\nOpen: http://localhost:8000\n\n**.NET:**\n```bash\ndotnet restore\ndotnet run\n```\nOpen: http://localhost:8000\n\n**Java:**\n```bash\nmvn clean package\nmvn cargo:run\n```\nOpen: http://localhost:8000\n\n### 4. Test a recurring payment\n\n1. Open the app in your browser\n2. Enter amount (e.g. `25.00`) and select **Recurring**\n3. Fill in customer details (name, email, address)\n4. Choose frequency (e.g. **Monthly**) and a start date\n5. Use a test card from [Test Cards](#test-cards) below\n6. Click **Submit** — note the `paymentMethodId` in the response\n7. That ID can be used for all future recurring charges\n\n---\n\n## API Endpoints\n\n### `GET /config`\n\nReturns a scoped GP API access token for client-side hosted field initialization. Token has `PMT_POST_Create_Single` permission — it can only tokenize cards, not process transactions.\n\n**Response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"accessToken\": \"uua7....\"\n  },\n  \"message\": \"Configuration retrieved successfully\",\n  \"timestamp\": \"2025-01-15T10:00:00.000Z\"\n}\n```\n\n---\n\n### `POST /process-payment`\n\nProcesses either a one-time payment or an initial recurring charge with customer data.\n\n#### One-time payment\n\n**Request body:**\n```json\n{\n  \"payment_token\": \"PMT_abc123...\",\n  \"amount\": 25.00,\n  \"currency\": \"USD\",\n  \"is_recurring\": false\n}\n```\n\n**Success response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"transaction_id\": \"TRN_abc123xyz\",\n    \"amount\": 25.00,\n    \"currency\": \"USD\",\n    \"status\": \"captured\",\n    \"message\": \"Payment processed successfully\"\n  }\n}\n```\n\n#### Recurring payment setup\n\n**Request body:**\n```json\n{\n  \"payment_token\": \"PMT_abc123...\",\n  \"amount\": 25.00,\n  \"currency\": \"USD\",\n  \"is_recurring\": true,\n  \"frequency\": \"Monthly\",\n  \"start_date\": \"2025-02-01\",\n  \"first_name\": \"Jane\",\n  \"last_name\": \"Smith\",\n  \"email\": \"jane.smith@example.com\",\n  \"phone\": \"555-0100\",\n  \"street_address\": \"123 Main St\",\n  \"city\": \"Atlanta\",\n  \"state\": \"GA\",\n  \"billing_zip\": \"30301\"\n}\n```\n\n**Success response:**\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"transaction_id\": \"TRN_abc123xyz\",\n    \"payment_method_id\": \"PMT_stored456\",\n    \"customer_id\": \"CUS_789xyz\",\n    \"amount\": 25.00,\n    \"currency\": \"USD\",\n    \"frequency\": \"Monthly\",\n    \"start_date\": \"2025-02-01\",\n    \"status\": \"active\",\n    \"message\": \"Initial payment successful. Recurring payment method stored.\"\n  },\n  \"message\": \"Recurring payment schedule created successfully\"\n}\n```\n\n**Supported frequency values:** `Weekly`, `Bi-Weekly`, `Monthly`, `Quarterly`, `Annually`\n\n**Error response (`422`):**\n```json\n{\n  \"success\": false,\n  \"error\": \"Payment declined: Insufficient funds\",\n  \"timestamp\": \"2025-01-15T10:00:00.000Z\"\n}\n```\n\n---\n\n## Test Cards\n\nUse these in sandbox (`GP_API_ENVIRONMENT=sandbox`). CVV: `123`. Expiry: any future date.\n\n| Brand | Card Number | Expected Result |\n|-------|-------------|-----------------|\n| Visa | 4263 9826 4026 9299 | Approved |\n| Mastercard | 5425 2334 2424 1200 | Approved |\n| Discover | 6011 0000 0000 0012 | Approved |\n| Declined | 4000 1200 0000 1154 | Declined |\n\n\u003e Sandbox transactions do not move real money.\n\n---\n\n## Project Structure\n\n```\nonline-recurring-payments/\n├── index.html                 # Shared frontend (served by all backends)\n├── LICENSE\n├── README.md\n│\n├── php/                       # Port 8000\n│   ├── .env.sample\n│   ├── composer.json\n│   ├── Dockerfile\n│   ├── PaymentUtils.php       # SDK config + shared helpers\n│   ├── config.php             # GET /config endpoint\n│   ├── process-payment.php    # POST /process-payment endpoint\n│   └── run.sh\n│\n├── nodejs/                    # Port 8000\n│   ├── .env.sample\n│   ├── package.json\n│   ├── Dockerfile\n│   ├── server.js              # Express app: /config, /process-payment\n│   ├── paymentUtils.js        # SDK config + recurring helpers\n│   └── run.sh\n│\n├── dotnet/                    # Port 8000\n│   ├── .env.sample\n│   ├── *.csproj\n│   ├── Program.cs             # ASP.NET Core app: all endpoints\n│   ├── Dockerfile\n│   └── wwwroot/               # Static frontend files\n│\n└── java/                      # Port 8000\n    ├── .env.sample\n    ├── pom.xml\n    ├── Dockerfile\n    └── src/\n        └── main/java/com/globalpayments/example/\n            ├── ConfigServlet.java\n            └── ProcessPaymentServlet.java\n```\n\n---\n\n## Environment Variables\n\n| Variable | Description | Example |\n|----------|-------------|---------|\n| `APP_ID` | Your GP API application ID | `UJqPrAhrDkGzzNoFInpzKqoI8vfZtGRV` |\n| `APP_KEY` | Your GP API application key | `zCFrbrn0NKly9sB4` |\n| `GP_API_ENVIRONMENT` | `sandbox` for testing, `production` for live | `sandbox` |\n\nCredentials are available in the [GP Developer Portal](https://developer.globalpayments.com) after creating an account.\n\n---\n\n## Troubleshooting\n\n**`401 Unauthorized` on `/config`**\nCredentials are invalid or for the wrong environment. Verify `APP_ID` and `APP_KEY` in `.env`. Confirm `GP_API_ENVIRONMENT` matches the credential set (sandbox vs production).\n\n**`422` — \"Payment declined\" on initial charge**\nThe test card was declined. Try a different card from the [Test Cards](#test-cards) table. Confirm `GP_API_ENVIRONMENT=sandbox` when using test cards.\n\n**Recurring charge returns \"Invalid payment method\"**\nThe `payment_method_id` from the initial charge must be stored and reused in subsequent charges. The stored credential expires if the sandbox session ends — run a new initial charge to get a fresh ID.\n\n**Missing customer fields validation error**\nRecurring payments require `first_name`, `last_name`, and `email`. `phone`, `street_address`, `city`, `state`, and `billing_zip` are optional but improve auth rates.\n\n**Port 8000 already in use**\nAnother process is binding the port. Check with `lsof -i :8000` and stop the conflicting process, or update the port in `run.sh` and the server file.\n\n**Node.js — `Cannot find module 'globalpayments-api'`**\nRun `npm install` before `npm start`. Confirm Node.js 18+ is installed: `node -v`.\n\n**Java build fails**\nRequires Java 17+ and Maven 3.8+. Verify with `java -version` and `mvn -version`. Run `mvn clean package` before `mvn cargo:run`.\n\n---\n\n## Features\n\n- Process one-time payments using GP API hosted fields\n- Process recurring payments with `StoredCredential` — no card re-entry after the initial charge\n- 4-language implementations (PHP, Node.js, .NET, Java)\n- Docker support with per-language containers\n\n---\n\n## Security Considerations\n\n- Store credentials in `.env` files, never commit to source control\n- API keys are never exposed to the frontend\n- All payment processing happens server-side\n- Use HTTPS in production\n- Stored credentials must comply with card network rules\n\n---\n\n## Resources\n\n- [Global Payments Developer Portal](https://developer.globalpayments.com/)\n- [GP-API Reference](https://developer.globalpayments.com/api/references-overview)\n- [Test Cards](https://developer.globalpayments.com/resources/test-cards)\n\n---\n\n## Community\n\n- 🌐 **Developer Portal** — [developer.globalpayments.com](https://developer.globalpayments.com)\n- 💬 **Discord** — [Join the community](https://discord.gg/myER9G9qkc)\n- 📋 **GitHub Discussions** — [github.com/orgs/globalpayments/discussions](https://github.com/orgs/globalpayments/discussions)\n- 📧 **Newsletter** — [Subscribe](https://www.globalpayments.com/en-gb/modals/newsletter)\n- 💼 **LinkedIn** — [Global Payments for Developers](https://www.linkedin.com/showcase/global-payments-for-developers/posts/?feedView=all)\n\nHave a question or found a bug? [Open an issue](https://github.com/globalpayments-samples/online-recurring-payments/issues) or reach out at [communityexperience@globalpay.com](mailto:communityexperience@globalpay.com).\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalpayments-samples%2Fonline-recurring-payments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobalpayments-samples%2Fonline-recurring-payments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobalpayments-samples%2Fonline-recurring-payments/lists"}