Skip to content
Merged
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 CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ that, `api_key` exists as `''` and there is no way to tell the consumer only sup
Unlike the misspelled `syncronized` that 2.0 dropped outright, these two are required and therefore set
by every consumer, and Sylius stores the gateway config keyed by them, so a hard rename would break
every existing shop. Other options (`payment_methods`, `auto_capture`,
`order_prefix`, `language`, `synchronized`, `agreement` → link `agreementId`, `branding_id`) are
`order_prefix`, `language`, `synchronized`, `agreement_id` → link `agreementId`, `branding_id`) are
defaulted. The closure constructs the SDK `Client` from the api key **and the `synchronized` flag** (or
accepts a prebuilt `Setono\Quickpay\Client\ClientInterface` via the optional `quickpay.client` option —
used by tests). Because `synchronized` is a constructor-only default on the SDK client, an injected
Expand All @@ -82,7 +82,7 @@ rather than silently overriding it. `payment_methods` accepts a string **or** a
normalized to Quickpay's comma-separated form by `normalizePaymentMethods()` — a `(string)` cast of a
list would have sent the literal `Array`, which reads as an allowlist of one unknown method and rejects
every payment; anything that is neither shape throws rather than being coerced. Empty configuration
normalizes to `null` — as `agreement` and `branding_id` already did — so the empty-string Payum default
normalizes to `null` — as `agreement_id` and `branding_id` already did — so the empty-string Payum default
never reaches `Api`, and `null` is what keeps the parameter off the request entirely.

### Actions (`src/Action/`)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ Integration — used to verify callback signatures). Other options are optional:
| `order_prefix` | `''` | Prepended to the Payum payment number to form the Quickpay order id. |
| `language` | `en` | Payment-window language. |
| `synchronized` | `false` | Run capture/refund/cancel synchronously instead of via callbacks. |
| `agreement` | `''` | Optional payment-window agreement id. |
| `agreement_id` | `''` | Optional payment-window agreement id. |
| `branding_id` | `''` | Optional payment-window branding id. |

The 1.x spellings `apikey` and `privatekey` are still accepted as deprecated aliases, so an existing
gateway configuration keeps working. They will be removed in 3.0.
The 1.x names `apikey`, `privatekey` and `agreement` are still accepted as deprecated aliases, so an
existing gateway configuration keeps working. They will be removed in 3.0.

```php
<?php
Expand Down
26 changes: 20 additions & 6 deletions docs/UPGRADE-2.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,26 @@ handling, and modernizes the test suite. This is a major release with breaking c

- **`merchant` was removed.** Quickpay API v10 authenticates with the API key alone; the option was
never used. Remove it from your gateway configuration (passing it is harmless — it is ignored).
- **The credentials are now `api_key` and `private_key`** (snake_case, like every other multi-word
option). The 1.x spellings `apikey` and `private_key` still work as **deprecated aliases**, so an
existing gateway configuration — including one stored in a database, as Sylius does — keeps working
untouched. Prefer the new names; the aliases go in 3.0.
- **Only the two credentials are required.** `language` is defaulted to `en`.
- **`agreement` is now optional** and maps to the payment-window `agreement_id`.
- **Options renamed to say what they are:**

| 1.x | 2.0 |
|-----|-----|
| `apikey` | `api_key` |
| `privatekey` | `private_key` |
| `agreement` | `agreement_id` |

The credentials are snake_case like every other multi-word option, and `agreement_id` matches its
sibling `branding_id` — both are optional integer payment-link ids.

All three 1.x names still work as **deprecated aliases**, so an existing gateway configuration —
including one stored in a database, as Sylius does — keeps working untouched. Prefer the new names;
the aliases go in 3.0.

Worth updating `agreement` sooner rather than later: unlike the credentials it is optional, so if the
aliases are ever removed while your config still uses the old name, it fails **silently** — the
payment link is created without an agreement id and Quickpay falls back to the account default.
- **Only the two credentials are required.** `language` is defaulted to `en`, and `agreement_id` is
optional.
- **New options:** `synchronized` (run capture/refund/cancel synchronously instead of relying on the
callback; default `false`, preserving 1.x behavior) and `branding_id` (payment-window branding).
- The misspelled, unused `syncronized` option was removed; use `synchronized`.
Expand Down
2 changes: 1 addition & 1 deletion examples/e2e/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ function e2e_payum(string $baseUrl): Payum
'payment_methods' => e2e_env('QUICKPAY_PAYMENT_METHODS', false),
'auto_capture' => e2e_bool('QUICKPAY_AUTO_CAPTURE') ? 1 : 0,
'synchronized' => e2e_bool('QUICKPAY_SYNCHRONIZED'),
'agreement' => e2e_env('QUICKPAY_AGREEMENT', false),
'agreement_id' => e2e_env('QUICKPAY_AGREEMENT', false),
'branding_id' => e2e_env('QUICKPAY_BRANDING_ID', false),
])
->getPayum();
Expand Down
29 changes: 18 additions & 11 deletions src/QuickpayGatewayFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected function populateConfig(ArrayObject $config): void
'language' => 'en',
'synchronized' => false,
// optional: maps to CreateLinkRequest::agreementId
'agreement' => '',
'agreement_id' => '',
// optional: maps to the Quickpay branding id on the payment link
'branding_id' => '',
];
Expand Down Expand Up @@ -93,22 +93,25 @@ protected function populateConfig(ArrayObject $config): void
paymentMethods: self::normalizePaymentMethods($config['payment_methods']),
language: (string) $config['language'],
autoCapture: (bool) (int) $config['auto_capture'],
agreementId: '' !== (string) $config['agreement'] ? (int) $config['agreement'] : null,
agreementId: '' !== (string) $config['agreement_id'] ? (int) $config['agreement_id'] : null,
brandingId: '' !== (string) $config['branding_id'] ? (int) $config['branding_id'] : null,
);
};
}
}

/**
* Every other multi-word option is snake_case (`payment_methods`, `auto_capture`, `order_prefix`,
* `branding_id`), so the credentials are `api_key` and `private_key`. The 1.x spellings `apikey` and
* `privatekey` still work.
* The option names say what they are: the credentials are `api_key` and `private_key` (snake_case,
* like `payment_methods`, `auto_capture`, `order_prefix`), and `agreement_id` matches its sibling
* `branding_id` — both are optional integer payment-link ids. The 1.x spellings `apikey`,
* `privatekey` and `agreement` still work.
*
* They are aliased rather than dropped because, unlike the misspelled `syncronized` that 2.0 removed
* outright, these two are required and therefore set by **every** consumer — and Sylius stores the
* gateway configuration as JSON keyed by exactly these names, so a hard rename would break every
* existing shop until its stored config was migrated. The aliases are deprecated and will go in 3.0.
* They are aliased rather than dropped because Sylius stores the gateway configuration as JSON keyed
* by exactly these names, so a hard rename would break every existing shop until its stored config
* was migrated. Unlike the misspelled `syncronized` that 2.0 removed outright — an option nobody had
* meaningfully set — these are load-bearing, and `agreement` fails *silently* if missed: it is
* optional, so a stale key resolves to `null` and the payment link is simply created without an
* agreement id, quietly falling back to the account default. The aliases are deprecated and go in 3.0.
*
* Must run before the defaults are applied: once `api_key` exists (as `''`), there is no longer any
* way to tell that the consumer only supplied the old spelling.
Expand All @@ -117,7 +120,11 @@ protected function populateConfig(ArrayObject $config): void
*/
private static function aliasDeprecatedOptions(ArrayObject $config): void
{
foreach (['apikey' => 'api_key', 'privatekey' => 'private_key'] as $deprecated => $current) {
foreach ([
'apikey' => 'api_key',
'privatekey' => 'private_key',
'agreement' => 'agreement_id',
] as $deprecated => $current) {
if ($config->offsetExists($deprecated) && !$config->offsetExists($current)) {
$config[$current] = $config[$deprecated];
}
Expand All @@ -134,7 +141,7 @@ private static function aliasDeprecatedOptions(ArrayObject $config): void
*
* Empty configuration — the default `''`, an empty list, or a list of blanks — becomes `null`, the
* value that leaves the restriction off the request altogether. The empty string is a Payum config
* artifact and stops here, exactly as `agreement` and `branding_id` do.
* artifact and stops here, exactly as `agreement_id` and `branding_id` do.
*
* @throws LogicException if the option is neither a string nor a list of strings
*/
Expand Down
23 changes: 23 additions & 0 deletions tests/QuickpayGatewayFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,35 @@ public function shouldAcceptTheDeprecatedCredentialOptionNames(): void
$config = $factory->createConfig([
'apikey' => 'old-api-key',
'privatekey' => 'old-private-key',
'agreement' => '266017',
]);

$api = $config['payum.api'](ArrayObject::ensureArrayObject($config));

self::assertInstanceOf(Api::class, $api);
self::assertSame('old-private-key', $api->getPrivateKey());
self::assertSame(266017, $api->getAgreementId());
}

/**
* `agreement` is optional, so a name that silently stopped being read would not throw — the payment
* link would just be created without an agreement id, falling back to the account default. Pin both
* directions.
*
* @test
*/
public function shouldReadTheAgreementIdUnderEitherName(): void
{
$factory = new QuickpayGatewayFactory();

self::assertSame(266017, self::createApi($factory, ['agreement_id' => '266017'])->getAgreementId());
self::assertSame(266017, self::createApi($factory, ['agreement' => '266017'])->getAgreementId());
self::assertNull(self::createApi($factory, [])->getAgreementId(), 'Unset stays unset');
self::assertSame(
266017,
self::createApi($factory, ['agreement' => '1', 'agreement_id' => '266017'])->getAgreementId(),
'The current name wins',
);
}

/**
Expand Down
Loading