Fix: Stripping Lunar's Stripe Driver with Boboko's Stripe Payment Driver
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Core\Payment\Support;
|
||||
|
||||
use Lunar\Models\Contracts\Currency as CurrencyContract;
|
||||
use Stripe\Charge;
|
||||
use Stripe\StripeClient;
|
||||
|
||||
/**
|
||||
* First-party replacement for Lunar\Stripe\Facades\Stripe +
|
||||
* Lunar\Stripe\Managers\StripeManager — lunarphp/stripe was removed once
|
||||
* Modules\Core\Payment\Drivers\StripePaymentDriver already replaced every
|
||||
* bit of Lunar's own Stripe payment flow (see that class's own docblock);
|
||||
* all that remained load-bearing from the package was raw API-client
|
||||
* access and amount conversion, neither of which is Lunar-specific. Only
|
||||
* the methods StripePaymentDriver actually called are kept — no
|
||||
* fetchOrCreateIntent()/cart-bound helpers, which belonged to Lunar's own
|
||||
* (unused) checkout flow.
|
||||
*
|
||||
* getClient()/getCharge() call the Stripe SDK directly rather than going
|
||||
* through a facade — StripePaymentDriver resolves this class via the
|
||||
* container instead, same as every other dependency it takes.
|
||||
*/
|
||||
class StripeManager
|
||||
{
|
||||
public function getClient(): StripeClient
|
||||
{
|
||||
return new StripeClient([
|
||||
'api_key' => config('services.stripe.key'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function getCharge(string $chargeId): Charge
|
||||
{
|
||||
return $this->getClient()->charges->retrieve($chargeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zero-decimal currencies, per Stripe. The amount sent to Stripe is the
|
||||
* major unit amount as-is.
|
||||
*
|
||||
* @see https://docs.stripe.com/currencies#zero-decimal
|
||||
*/
|
||||
protected const ZERO_DECIMAL_CURRENCIES = [
|
||||
'bif', 'clp', 'djf', 'gnf', 'jpy', 'kmf', 'krw', 'mga', 'pyg',
|
||||
'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf',
|
||||
];
|
||||
|
||||
/**
|
||||
* Three-decimal currencies, per Stripe. The amount sent to Stripe is the
|
||||
* major unit amount multiplied by 1000.
|
||||
*
|
||||
* @see https://docs.stripe.com/currencies#three-decimal
|
||||
*/
|
||||
protected const THREE_DECIMAL_CURRENCIES = ['bhd', 'jod', 'kwd', 'omr', 'tnd'];
|
||||
|
||||
/**
|
||||
* HUF, TWD and UGX are ISO zero-decimal currencies, but Stripe still
|
||||
* requires amounts to be sent as if they had two decimal places.
|
||||
*
|
||||
* @see https://docs.stripe.com/currencies#special-cases
|
||||
*/
|
||||
protected const SPECIAL_ZERO_DECIMAL_CURRENCIES = ['huf', 'twd', 'ugx'];
|
||||
|
||||
/**
|
||||
* Convert a Lunar price value to the amount expected by Stripe.
|
||||
*
|
||||
* Lunar stores prices as integers scaled by `Currency::decimal_places`,
|
||||
* which merchants can set independently of what Stripe expects for a
|
||||
* given currency. This converts back to the major unit amount first,
|
||||
* then re-scales it to whatever sub-unit Stripe requires for the
|
||||
* currency, so the result is correct regardless of how the merchant has
|
||||
* configured `Currency::decimal_places`.
|
||||
*
|
||||
* @see https://docs.stripe.com/currencies
|
||||
*/
|
||||
public static function toStripeAmount(int $value, CurrencyContract $currency): int
|
||||
{
|
||||
return self::rescale($value, max($currency->decimal_places, 0), self::stripeDecimalPlaces($currency));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an amount received from Stripe back to a Lunar price value,
|
||||
* scaled by `Currency::decimal_places`. Inverse of `toStripeAmount()`.
|
||||
*/
|
||||
public static function fromStripeAmount(int $amount, CurrencyContract $currency): int
|
||||
{
|
||||
return self::rescale($amount, self::stripeDecimalPlaces($currency), max($currency->decimal_places, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of decimal places Stripe expects amounts in for a currency.
|
||||
*/
|
||||
protected static function stripeDecimalPlaces(CurrencyContract $currency): int
|
||||
{
|
||||
$code = strtolower($currency->code);
|
||||
|
||||
// UGX is also in the zero-decimal list; the special case takes precedence.
|
||||
if (in_array($code, self::SPECIAL_ZERO_DECIMAL_CURRENCIES, true)) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (in_array($code, self::ZERO_DECIMAL_CURRENCIES, true)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (in_array($code, self::THREE_DECIMAL_CURRENCIES, true)) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
protected static function rescale(int $value, int $fromDecimalPlaces, int $toDecimalPlaces): int
|
||||
{
|
||||
$exponent = $toDecimalPlaces - $fromDecimalPlaces;
|
||||
|
||||
if ($exponent >= 0) {
|
||||
return $value * (10 ** $exponent);
|
||||
}
|
||||
|
||||
$divisor = 10 ** (-$exponent);
|
||||
|
||||
return intdiv(abs($value) + intdiv($divisor, 2), $divisor) * ($value < 0 ? -1 : 1);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user