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
8 changes: 3 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ jobs:
- "0"
- "1"
php-version:
- "5.6"
- "7.0"
- "7.1"
# https://docs.stripe.com/sdks/versioning?lang=php
- "7.2"
- "7.3"
- "7.4"
Expand All @@ -123,6 +121,7 @@ jobs:
- "8.2"
- "8.3"
- "8.4"
- "8.5"
name: Tests (php@${{ matrix.php-version }}, AUTOLOAD=${{ matrix.autoload }})

steps:
Expand Down Expand Up @@ -167,8 +166,7 @@ jobs:
name: Publish
if: >-
(github.event_name == 'workflow_dispatch' || github.event_name == 'push') &&
startsWith(github.ref, 'refs/tags/v') &&
endsWith(github.actor, '-stripe')
startsWith(github.ref, 'refs/tags/v')
runs-on: "ubuntu-24.04"
permissions:
contents: read
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ API.

## Requirements

PHP 5.6.0 and later.
PHP 7.2.0 and later.

Note that per our [language version support policy](https://docs.stripe.com/sdks/versioning?lang=php#stripe-sdk-language-version-support-policy), support for PHP 5.6, 7.0, and 7.1 will be removed in the March 2026 major version.
Note that per our [language version support policy](https://docs.stripe.com/sdks/versioning?lang=php#stripe-sdk-language-version-support-policy), support for PHP 7.2 and 7.3 will be removed soon, so upgrade your runtime if you're able to.

Additional PHP versions will be dropped in future major versions, so upgrade to supported versions if possible.

Expand Down
13 changes: 8 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
}
],
"require": {
"php": ">=5.6.0",
"php": ">=7.2.0",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*"
Expand All @@ -28,7 +28,10 @@
"autoload": {
"psr-4": {
"Stripe\\": "lib/"
}
},
"files": [
"lib/version_check.php"
]
},
"autoload-dev": {
"psr-4": {
Expand All @@ -45,9 +48,9 @@
},
"config": {
"audit": {
"ignore": {
"PKSA-z3gr-8qht-p93v": "PHPUnit is only a dev dependency. Temporarily ignore PHPUnit security advisory to ensure continued support for PHP 5.6 in CI."
}
"ignore": {
"PKSA-z3gr-8qht-p93v": "PHPUnit is only a dev dependency. Temporarily ignore PHPUnit security advisory to ensure continued support for PHP 5.6 in CI."
}
}
}
}
3 changes: 3 additions & 0 deletions init.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

require __DIR__ . '/lib/version_check.php';

require __DIR__ . '/lib/Util/ApiVersion.php';

// Stripe singleton
Expand All @@ -18,6 +20,7 @@
require __DIR__ . '/lib/Util/Util.php';
require __DIR__ . '/lib/Util/EventTypes.php';
require __DIR__ . '/lib/Util/EventNotificationTypes.php';
require __DIR__ . '/lib/Util/Int64.php';
require __DIR__ . '/lib/Util/ObjectTypes.php';

// HttpClient
Expand Down
27 changes: 21 additions & 6 deletions lib/Service/AbstractService.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,24 +70,39 @@ private static function formatParams($params)
return $params;
}

protected function request($method, $path, $params, $opts)
protected function request($method, $path, $params, $opts, $schemas = null)
{
return $this->getClient()->request($method, $path, self::formatParams($params), $opts);
$params = self::formatParams($params);
if (null !== $schemas && isset($schemas['request_schema'])) {
$params = \Stripe\Util\Int64::coerceRequestParams($params, $schemas['request_schema']);
}

return $this->getClient()->request($method, $path, $params, $opts);
}

protected function requestStream($method, $path, $readBodyChunkCallable, $params, $opts)
{
return $this->getStreamingClient()->requestStream($method, $path, $readBodyChunkCallable, self::formatParams($params), $opts);
}

protected function requestCollection($method, $path, $params, $opts)
protected function requestCollection($method, $path, $params, $opts, $schemas = null)
{
return $this->getClient()->requestCollection($method, $path, self::formatParams($params), $opts);
$params = self::formatParams($params);
if (null !== $schemas && isset($schemas['request_schema'])) {
$params = \Stripe\Util\Int64::coerceRequestParams($params, $schemas['request_schema']);
}

return $this->getClient()->requestCollection($method, $path, $params, $opts);
}

protected function requestSearchResult($method, $path, $params, $opts)
protected function requestSearchResult($method, $path, $params, $opts, $schemas = null)
{
return $this->getClient()->requestSearchResult($method, $path, self::formatParams($params), $opts);
$params = self::formatParams($params);
if (null !== $schemas && isset($schemas['request_schema'])) {
$params = \Stripe\Util\Int64::coerceRequestParams($params, $schemas['request_schema']);
}

return $this->getClient()->requestSearchResult($method, $path, $params, $opts);
}

protected function buildPath($basePath, ...$ids)
Expand Down
13 changes: 12 additions & 1 deletion lib/StripeObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,16 @@ public function refreshFrom($values, $opts, $partial = false, $apiMode = 'v1')
$values = $values->toArray();
}

// Apply int64_string response coercion on raw values before hydration.
// V2 resource classes declare fieldEncodings() with metadata about which
// fields are int64_string (wire format: JSON string, SDK type: PHP int).
if (\method_exists(static::class, 'fieldEncodings')) {
$encodings = static::fieldEncodings();
if (!empty($encodings)) {
$values = Util\Int64::coerceResponseValues($values, $encodings);
}
}

// Wipe old state before setting new. This is useful for e.g. updating a
// customer, where there is no persistent card parameter. Mark those values
// which don't persist as transient
Expand Down Expand Up @@ -331,7 +341,8 @@ public function updateAttributes($values, $opts = null, $dirty = true, $apiMode
// This is necessary in case metadata is empty, as PHP arrays do
// not differentiate between lists and hashes, and we consider
// empty arrays to be lists.
if (('metadata' === $k) && \is_array($v)) {
// The same applies to the previous_attributes attribute.
if (('metadata' === $k || 'previous_attributes' === $k) && \is_array($v)) {
$this->_values[$k] = StripeObject::constructFrom($v, $opts, $apiMode);
} else {
$this->_values[$k] = Util\Util::convertToStripeObject($v, $opts, $apiMode);
Expand Down
128 changes: 128 additions & 0 deletions lib/Util/Int64.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace Stripe\Util;

/**
* Handles coercion between PHP int and JSON string for int64_string fields.
*
* V2 API fields marked as int64_string are transmitted as JSON strings on
* the wire but exposed as PHP ints in the SDK.
*/
class Int64
{
/**
* Coerce outbound request params: convert PHP ints to strings where
* the request schema indicates an int64_string field.
*
* @param mixed $params
* @param array $schema e.g. ['kind' => 'object', 'fields' => ['amount' => ['kind' => 'int64_string']]]
*
* @return mixed
*/
public static function coerceRequestParams($params, $schema)
{
if (null === $params) {
return null;
}

if (!isset($schema['kind'])) {
return $params;
}

if ('int64_string' === $schema['kind']) {
if (\is_int($params)) {
return (string) $params;
}

return $params;
}

if ('array' === $schema['kind'] && isset($schema['items'])) {
if (\is_array($params)) {
$result = [];
foreach ($params as $key => $value) {
$result[$key] = self::coerceRequestParams($value, $schema['items']);
}

return $result;
}

return $params;
}

if ('object' === $schema['kind'] && isset($schema['fields'])) {
if (\is_array($params)) {
$result = $params;
foreach ($schema['fields'] as $field => $fieldSchema) {
if (\array_key_exists($field, $result)) {
$result[$field] = self::coerceRequestParams($result[$field], $fieldSchema);
}
}

return $result;
}

return $params;
}

return $params;
}

/**
* Coerce inbound response values: convert JSON strings to PHP ints where
* the field encodings indicate an int64_string field.
*
* @param mixed $values
* @param array $encodings e.g. ['amount' => ['kind' => 'int64_string'], 'nested' => ['kind' => 'object', 'fields' => [...]]]
*
* @return mixed
*/
public static function coerceResponseValues($values, $encodings)
{
if (!\is_array($values)) {
return $values;
}

foreach ($encodings as $field => $encoding) {
if (!\array_key_exists($field, $values)) {
continue;
}

$value = $values[$field];

if (!isset($encoding['kind'])) {
continue;
}

if ('int64_string' === $encoding['kind']) {
if (\is_string($value) && \is_numeric($value)) {
$values[$field] = (int) $value;
}
} elseif ('array' === $encoding['kind'] && isset($encoding['items'])) {
if (\is_array($value)) {
foreach ($value as $i => $item) {
if (!isset($encoding['items']['kind'])) {
continue;
}

if ('int64_string' === $encoding['items']['kind']) {
if (\is_string($item) && \is_numeric($item)) {
$values[$field][$i] = (int) $item;
}
} elseif ('object' === $encoding['items']['kind'] && isset($encoding['items']['fields'])) {
if (\is_array($item)) {
$values[$field][$i] = self::coerceResponseValues($item, $encoding['items']['fields']);
}
}
}
}
} elseif ('object' === $encoding['kind'] && isset($encoding['fields'])) {
if (\is_array($value)) {
$values[$field] = self::coerceResponseValues($value, $encoding['fields']);
}
}
}

return $values;
}
}
9 changes: 9 additions & 0 deletions lib/version_check.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

// see: https://docs.stripe.com/sdks/versioning?lang=php

if (\PHP_VERSION_ID < 70200) {
throw new RuntimeException(
'The Stripe SDK requires PHP 7.2.0 or later. You are running PHP ' . \PHP_VERSION . '.'
);
}
Loading
Loading