Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ This repository is a Laravel package. Keep the package focused, idiomatic, and e
## SimplePay Architecture

- The package follows the official SimplePay PHP SDKs (API v2, published at https://simplepay.hu/fejlesztoknek/). `official-docs/` holds the reference PDFs; it is not part of the dist archive.
- `src/SimplePay.php` sits behind the `SimplePay` facade and exposes the services in `src/Services`: `PaymentService` (start, finish, refund, query, cancel, qvik/EAM, Apple Pay), `CardStorageService` (OneClick `do`, recurring, card and token query/cancel), `AutoPaymentService` (merchant-side card data) and `RtpService` (Request to Pay).
- `src/SimplePay.php` sits behind the `SimplePay` facade and exposes the services in `src/Services`: `PaymentService` (start, finish, refund, query, cancel, qvik/EAM, Apple Pay, merchant-side Google Pay with `doGooglePay()` and `googlePayConfig()`), `CardStorageService` (OneClick `do`, recurring, card and token query/cancel), `AutoPaymentService` (merchant-side card data) and `RtpService` (Request to Pay).
- Request and response payloads are readonly DTOs in `src/Dto`: `toArray()` builds the API body including `salt` and `sdkVersion`, `fromArray()` maps the response. Enums live in `src/Enums`; `ErrorCode` mirrors the official error code list with Hungarian descriptions.
- `src/Support/Client.php` signs every request with HMAC-SHA384 (`Support/Signature`), verifies the response signature and throws `SimplePayApiException` when `errorCodes` is not empty; the exception keeps the response body (`getResponseData()`), e.g. the `redirectUrl` of a 3DS challenge (3004).
- `src/Support/Client.php` signs every request with HMAC-SHA384 (`Support/Signature`), verifies the response signature and throws `SimplePayApiException` when `errorCodes` is not empty; the exception keeps the response body (`getResponseData()`), e.g. the `redirectUrl` of a 3DS challenge (3004). `doGooglePay()` turns a 3004 with a `redirectUrl` into a normal `GooglePayResponse`.
- Merchant credentials are configured per currency (`simplepay.merchants`) and resolved by `Support/MerchantResolver`; IPN bodies without `currency` are resolved by their `merchant` field.
- Routes: `POST simplepay/ipn` (`VerifySimplePaySignature` middleware; `SimplePayWebhookController` dispatches the events in `src/Events`) and `GET simplepay/back` (`SimplePayBackController`, response customisable through the `BackUrlResponse` contract).
- Consumer documentation is bilingual: keep `examples/en/` and `examples/hu/` in sync with each other and with the README.
63 changes: 60 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,26 @@ $response->timeout; // Payment timeout
return redirect($response->paymentUrl);
```

### Event-Specific Redirect URLs

SimplePay returns the customer to `url` after every outcome. Pass `urls` to use a separate page per outcome; SimplePay ignores `url` when `urls` is sent:

```php
use Netipar\SimplePay\Dto\RedirectUrls;

$response = SimplePay::payment()->start(new PaymentRequest(
// ... same as above
urls: new RedirectUrls(
success: route('payment.success'),
fail: route('payment.failed'),
cancel: route('checkout'),
timeout: route('payment.timeout'),
),
));
```

The order reference of a failed transaction can be sent again until the first successful payment; every start creates a new SimplePay transaction ID.

### Two-Step Payment (Authorize + Finish)

```php
Expand Down Expand Up @@ -261,6 +281,35 @@ $result = SimplePay::payment()->doApplePay(new ApplePayDoRequest(
));
```

### Google Pay on Your Checkout Page

Google Pay is also available on the SimplePay payment page with `PaymentMethod::CARD`. To show the Google Pay button on your own checkout page, render it with `googlePayConfig()` and charge the token from your backend:

```php
use Netipar\SimplePay\Dto\GooglePayDoRequest;

// Checkout page: the values of the Google Pay button
$googlePay = SimplePay::payment()->googlePayConfig(Currency::HUF, 5000);

// Backend endpoint called by the button with the Base64 encoded Google Pay token
$validated = $request->validate(['googlePayToken' => ['required', 'string']]);

$response = SimplePay::payment()->doGooglePay(new GooglePayDoRequest(
currency: Currency::HUF,
total: 5000,
orderRef: 'ORDER-GP-001',
customerEmail: 'customer@example.com',
googlePayToken: $validated['googlePayToken'],
url: route('simplepay.back'), // where SimplePay returns the customer after a 3DS challenge
));

if ($response->requiresChallenge()) {
// Send the browser to $response->redirectUrl
}
```

Set `SIMPLEPAY_GOOGLE_PAY_MERCHANT_ID` and `SIMPLEPAY_GOOGLE_PAY_MERCHANT_NAME` from the Google Pay & Wallet Console. The full flow with the JavaScript button is in the [Google Pay example](examples/en/google-pay.md).

### EAM / Qvik Payment

```php
Expand All @@ -287,6 +336,7 @@ $result = SimplePay::rtp()->start(new RtpStartRequest(
orderRef: 'RTP-2026-001',
customerEmail: 'customer@example.com',
url: route('simplepay.back'),
customer: 'Customer Name',
));

// Query RTP status
Expand All @@ -299,6 +349,8 @@ SimplePay::rtp()->refund(Currency::HUF, $transactionId, 15000);
SimplePay::rtp()->reverse(Currency::HUF, $transactionId);
```

Request to Pay back redirects can carry `BackEvent::Pending`: the customer gave the bank account number, but the transfer has not arrived yet. `RtpStartRequest` accepts `urls` with a `pending` URL as well.

## IPN Webhook

The package automatically registers a `POST /simplepay/ipn` route with signature verification middleware. Listen to payment events in your application:
Expand Down Expand Up @@ -362,6 +414,7 @@ class SimplePayBackResponse implements BackUrlResponse
BackEvent::Fail => redirect()->route('payment.failed'),
BackEvent::Cancel => redirect()->route('checkout'),
BackEvent::Timeout => redirect()->route('payment.timeout'),
BackEvent::Pending => redirect()->route('payment.pending'), // Request to Pay only
};
}
}
Expand All @@ -374,7 +427,7 @@ Always use enum cases instead of raw strings:
- **`Currency`** -- `HUF`, `EUR`, `USD`
- **`PaymentMethod`** -- `CARD`, `WIRE`, `EAM`
- **`PaymentStatus`** -- `INIT`, `FINISHED`, `AUTHORIZED`, `NOTAUTHORIZED`, `INPAYMENT`, `CANCELLED`, `TIMEOUT`, `INFRAUD`, `FRAUD`, `REFUND`, `REVERSED`
- **`BackEvent`** -- `SUCCESS`, `FAIL`, `CANCEL`, `TIMEOUT`
- **`BackEvent`** -- `SUCCESS`, `FAIL`, `CANCEL`, `TIMEOUT`, `PENDING` (Request to Pay)
- **`TransactionType`** -- `CIT` (Customer Initiated), `MIT` (Merchant Initiated), `REC` (Recurring)
- **`ErrorCode`** -- 358+ error codes with Hungarian descriptions

Expand Down Expand Up @@ -424,6 +477,8 @@ SIMPLEPAY_BACK_URL=https://yourdomain.com/payment/back
SIMPLEPAY_TIMEOUT=600
SIMPLEPAY_LOG_CHANNEL=simplepay
SIMPLEPAY_AUTO_CHALLENGE=true
SIMPLEPAY_GOOGLE_PAY_MERCHANT_ID=your_google_pay_merchant_id
SIMPLEPAY_GOOGLE_PAY_MERCHANT_NAME="Your Shop"
```

Multi-currency support -- each currency has its own merchant credentials:
Expand Down Expand Up @@ -457,7 +512,7 @@ Usage in Blade, Vue.js, or React:
<img src="/vendor/simplepay/logos/simplepay_200x50.png" alt="SimplePay" />
```

Available logos: SimplePay (multiple sizes/orientations + white variants), Visa, Mastercard, Maestro, Amex, Apple Pay, Google Pay, Qvik, OTP SZÉP. Pre-composed card type combinations are also included. See [examples](examples/en/logos-and-assets.md) for Blade, Vue.js, and React checkout page examples.
Available logos: SimplePay (multiple sizes/orientations + white variants, and an SVG of the vertical logo), Visa, Mastercard, Maestro, Amex, Apple Pay, Google Pay, Qvik, OTP SZÉP. Pre-composed card type combinations are also included. See [examples](examples/en/logos-and-assets.md) for Blade, Vue.js, and React checkout page examples.

## Logging

Expand All @@ -475,7 +530,7 @@ Set `SIMPLEPAY_LOG_CHANNEL` to any Laravel log channel name. Example dedicated c

| Service | Access | Purpose |
|---|---|---|
| `PaymentService` | `SimplePay::payment()` | Standard payments, refunds, queries, Apple Pay, EAM |
| `PaymentService` | `SimplePay::payment()` | Standard payments, refunds, queries, Apple Pay, Google Pay, EAM |
| `CardStorageService` | `SimplePay::cardStorage()` | OneClick, recurring, card/token management |
| `AutoPaymentService` | `SimplePay::autoPayment()` | PCI-DSS direct card payments |
| `RtpService` | `SimplePay::rtp()` | Request to Pay (bank transfers) |
Expand All @@ -484,6 +539,8 @@ Set `SIMPLEPAY_LOG_CHANNEL` to any Laravel log channel name. Example dedicated c

This package is built based on the **SimplePay PHP SDK v2.1** (API v2). All API requests include the `sdkVersion: SimplePay_PHP_SDK_2.1_Laravel` identifier.

The merchant-side Google Pay flow follows the Payment PHP SDK 2.1.5 (2026-06-27) and the SimplePay Google Pay documentation (2026-09-01); the `urls` and `pending` handling follows the Request to Pay documentation (2026-08-17).

## Examples

- [English examples](examples/en/)
Expand Down
16 changes: 16 additions & 0 deletions config/simplepay.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@

'log_channel' => env('SIMPLEPAY_LOG_CHANNEL'),

/*
|--------------------------------------------------------------------------
| Google Pay
|--------------------------------------------------------------------------
|
| Merchant-side Google Pay acceptance through SimplePay. The merchant ID
| and name come from the Google Pay & Wallet Console; the checkout page
| needs them to render the Google Pay button.
|
*/

'google_pay' => [
'merchant_id' => env('SIMPLEPAY_GOOGLE_PAY_MERCHANT_ID'),
'merchant_name' => env('SIMPLEPAY_GOOGLE_PAY_MERCHANT_NAME'),
],

/*
|--------------------------------------------------------------------------
| Auto Challenge
Expand Down
5 changes: 5 additions & 0 deletions examples/en/back-url.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class SimplePayBackResponse implements BackUrlResponse
BackEvent::Fail => redirect()->route('payment.failed'),
BackEvent::Cancel => redirect()->route('payment.cancelled'),
BackEvent::Timeout => redirect()->route('payment.timeout'),
BackEvent::Pending => redirect()->route('payment.pending'),
};
}
}
Expand Down Expand Up @@ -81,6 +82,9 @@ The `BackResponse` object contains the following properties:
| `BackEvent::Fail` | Payment failed |
| `BackEvent::Cancel` | Customer cancelled the payment |
| `BackEvent::Timeout` | Payment timed out |
| `BackEvent::Pending` | Request to Pay only: the bank account number is given, the transfer has not arrived yet |

A `match` over `$back->event` has to cover `BackEvent::Pending` as well, otherwise a Request to Pay redirect throws an `UnhandledMatchError`.

## Example: Advanced Handling

Expand All @@ -102,6 +106,7 @@ class SimplePayBackResponse implements BackUrlResponse
BackEvent::Fail => $this->handleFailure($back),
BackEvent::Cancel => $this->handleCancel($back),
BackEvent::Timeout => $this->handleTimeout($back),
BackEvent::Pending => response()->view('payment.pending', ['orderRef' => $back->orderRef]),
};
}

Expand Down
20 changes: 20 additions & 0 deletions examples/en/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ SIMPLEPAY_EUR_SECRET_KEY=your_eur_secret_key
SIMPLEPAY_BACK_URL=https://yourdomain.com/payment/back
SIMPLEPAY_LOG_CHANNEL=simplepay
SIMPLEPAY_TIMEOUT=600
SIMPLEPAY_GOOGLE_PAY_MERCHANT_ID=your_google_pay_merchant_id
SIMPLEPAY_GOOGLE_PAY_MERCHANT_NAME="Your Shop"
```

## Configuration Structure
Expand Down Expand Up @@ -109,6 +111,20 @@ return [
*/
'log_channel' => env('SIMPLEPAY_LOG_CHANNEL'),

/*
|--------------------------------------------------------------------------
| Google Pay
|--------------------------------------------------------------------------
|
| The Google Pay merchant ID and name for the Google Pay button on your
| own checkout page.
|
*/
'google_pay' => [
'merchant_id' => env('SIMPLEPAY_GOOGLE_PAY_MERCHANT_ID'),
'merchant_name' => env('SIMPLEPAY_GOOGLE_PAY_MERCHANT_NAME'),
],

/*
|--------------------------------------------------------------------------
| Auto Challenge
Expand Down Expand Up @@ -173,3 +189,7 @@ The default value is 600 seconds (10 minutes).
## Auto Challenge

When `auto_challenge` is enabled, the package automatically handles 3D Secure challenge flows during card payments. This is enabled by default and is recommended for most use cases.

## Google Pay

`google_pay.merchant_id` and `google_pay.merchant_name` come from the Google Pay & Wallet Console. They are only needed when the Google Pay button is on your own checkout page; see [Google Pay on Your Checkout Page](google-pay.md).
Loading
Loading