Skip to content

Commit c84610f

Browse files
authored
Add stripe amount class and fileing stripe library files (#128)
* fix wrong remove gender seeder table follows comment * change stripe config from "service.stripe." to "stripe." /Users/tszyuloveyou/Projects/New-Official-Website/tests/Feature/Library/Stripe/PriceTest.php * move the stripe webhock controller and verify signature webhock middleware relation tests to Labrary\Stripe folder and update webhocks route * fix auth header wrong content expect * add more configs to stripe for price and payment amount * add stripe amount class to stripe library * fix stripe library bass class wrong version logic * fix checkout class wrong path name * change stripe prive class currency from hardcode to use config * change admission_test_prices and admission_test_orders table decimal total and places from hardcode to stripe library amount method * fix missing tests logic and fix errors * update readme * fix coding style
1 parent 58075fb commit c84610f

File tree

40 files changed

+557
-168
lines changed

40 files changed

+557
-168
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,20 @@ Please read database/README.md for database setup and details.
4848
1. 單一責任原則在function多過class
4949
2. fat controller skinny model,除非多過一個地方會用到同一功能
5050

51-
### TOdo
51+
### Payment cards for Stripe UAT
52+
https://docs.stripe.com/testing
53+
54+
### Todo
5255

5356
#### stage 1 permission system, nav, custom web page and admission test
5457

58+
- change admission_test_prices table stripe_id and synced_to_stripe columns to stripe_one_time_type_id and synced_one_time_type_to_stripe
59+
- change (*)_prices.price to (*)_prices.value and addresses.address to addresses.value
60+
- change members table to members and member_orders tables
5561
- update candidate store method to support select product and contact stripe
62+
- add stripe checkout web hock handle
63+
- change quota validity months to inside product and order table
64+
- add details, candidate and result module under admission test and change permission from admission test module to details, candidate and result
5665
- Add admin exchange and refund for admin admission test orders
5766
- add reschedule charges create product
5867
- add reschedule charges create product price
@@ -67,6 +76,7 @@ Please read database/README.md for database setup and details.
6776
- admin user add admission test orders list and create order button
6877
- Add assign roles function to admin user show
6978
- Admin team show page add team member list and relation role
79+
- change whatsapp from twilio to whatsapp cloud api (coming soon)
7080

7181
#### stage 2 third party iq test result
7282

@@ -153,6 +163,7 @@ Please read database/README.md for database setup and details.
153163
- Add admin shop products update
154164
- Add admin shop product options store
155165
- Add admin shop product options update
166+
- Add admin shop product options price store
156167
- Add admin shop orders create
157168
- Add admin shop orders index
158169
- Add admin shop order store status
@@ -170,4 +181,8 @@ Please read database/README.md for database setup and details.
170181

171182
#### stage 7 analytics (coming soon)
172183

173-
#### stage 8 contest (coming soon)
184+
#### stage 8 contest (coming soon)
185+
186+
#### unimportant
187+
188+
- takeout Strip Library from app folder to make a standalone composer package on standalone repo.

app/Http/Requests/Admin/AdmissionTest/Order/StoreRequest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Http\Requests\Admin\AdmissionTest\Order;
44

5+
use App\Library\Stripe\Rules\Amount\Other;
56
use App\Models\AdmissionTest;
67
use App\Models\OtherPaymentGateway;
78
use App\Models\User;
@@ -52,7 +53,7 @@ function (string $attribute, mixed $value, Closure $fail) use ($request) {
5253
],
5354
'product_name' => 'nullable|string|max:255',
5455
'price_name' => 'nullable|string|max:255',
55-
'price' => ['required', Rule::numeric()->min(0.01)->max(99999.99)->decimal(0, 2)],
56+
'price' => ['required', new Other],
5657
'minimum_age' => 'nullable|integer|min:1|max:255',
5758
'maximum_age' => 'nullable|integer|min:1|max:255',
5859
'quota' => 'required|integer|min:1|max:255',

app/Http/Requests/Admin/AdmissionTest/PriceRequest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace App\Http\Requests\Admin\AdmissionTest;
44

5+
use App\Library\Stripe\Rules\Amount\Stripe;
56
use Illuminate\Foundation\Http\FormRequest;
6-
use Illuminate\Validation\Rule;
77

88
class PriceRequest extends FormRequest
99
{
@@ -19,7 +19,7 @@ public function rules(): array
1919
'start_at' => 'nullable|date',
2020
];
2121
if ($this->method() == 'POST') {
22-
$return['price'] = ['required', Rule::numeric()->min(0.01)->max(99999.99)->decimal(0, 2)];
22+
$return['price'] = ['required', new Stripe];
2323
}
2424

2525
return $return;

app/Http/Requests/Admin/AdmissionTest/ProductRequest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
namespace App\Http\Requests\Admin\AdmissionTest;
44

5+
use App\Library\Stripe\Rules\Amount\Stripe;
56
use Illuminate\Foundation\Http\FormRequest;
6-
use Illuminate\Validation\Rule;
77

88
class ProductRequest extends FormRequest
99
{
@@ -33,7 +33,7 @@ public function rules(): array
3333
}
3434
if ($this->method() == 'POST') {
3535
$return['price_name'] = 'nullable|string|max:255';
36-
$return['price'] = ['required', Rule::numeric()->min(0.01)->max(99999.99)->decimal(0, 2)];
36+
$return['price'] = ['required', new Stripe];
3737
}
3838

3939
return $return;

app/Library/Stripe/Amount.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
namespace App\Library\Stripe;
4+
5+
use App\Library\Stripe\Exceptions\UnsupportedCurrency;
6+
7+
class Amount
8+
{
9+
public static $supportCurrencies = [
10+
'USD', 'AED', 'AFN', 'ALL', 'AMD', 'ANG', 'AOA', 'ARS', 'AUD',
11+
'AWG', 'AZN', 'BAM', 'BBD', 'BDT', 'BGN', 'BIF', 'BMD', 'BND',
12+
'BOB', 'BRL', 'BSD', 'BWP', 'BYN', 'BZD', 'CAD', 'CDF', 'CHF',
13+
'CLP', 'CNY', 'COP', 'CRC', 'CVE', 'CZK', 'DJF', 'DKK', 'DOP',
14+
'DZD', 'EGP', 'ETB', 'EUR', 'FJD', 'FKP', 'GBP', 'GEL', 'GIP',
15+
'GMD', 'GNF', 'GTQ', 'GYD', 'HKD', 'HNL', 'HTG', 'HUF', 'IDR',
16+
'ILS', 'INR', 'ISK', 'JMD', 'JPY', 'KES', 'KGS', 'KHR', 'KMF',
17+
'KRW', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', 'LSL', 'MAD',
18+
'MDL', 'MGA', 'MKD', 'MMK', 'MNT', 'MOP', 'MUR', 'MVR', 'MWK',
19+
'MXN', 'MYR', 'MZN', 'NAD', 'NGN', 'NIO', 'NOK', 'NPR', 'NZD',
20+
'PAB', 'PEN', 'PGK', 'PHP', 'PKR', 'PLN', 'PYG', 'QAR', 'RON',
21+
'RSD', 'RUB', 'RWF', 'SAR', 'SBD', 'SCR', 'SEK', 'SGD', 'SHP',
22+
'SLE', 'SOS', 'SRD', 'STD', 'SZL', 'THB', 'TJS', 'TOP', 'TRY',
23+
'TTD', 'TWD', 'TZS', 'UAH', 'UGX', 'UYU', 'UZS', 'VND', 'VUV',
24+
'WST', 'XAF', 'XCD', 'XCG', 'XOF', 'XPF', 'YER', 'ZAR', 'ZMW',
25+
];
26+
27+
public static $eurSubCurrencies = [
28+
'AD', 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FO', 'FI',
29+
'FR', 'DE', 'GI', 'GR', 'GL', 'GG', 'VA', 'HU', 'IS', 'IE', 'IM',
30+
'IT', 'JE', 'LV', 'LI', 'LT', 'LU', 'MT', 'MC', 'NL', 'NO', 'PL',
31+
'PT', 'RO', 'PM', 'SM', 'SK', 'SI', 'ES', 'SE', 'TR', 'GB',
32+
];
33+
34+
public static $specialDecimalCurrencies = [
35+
'BIF' => 0,
36+
'CLP' => 0,
37+
'DJF' => 0,
38+
'GNF' => 0,
39+
'JPY' => 0,
40+
'KMF' => 0,
41+
'KRW' => 0,
42+
'MGA' => 0,
43+
'PYG' => 0,
44+
'RWF' => 0,
45+
'UGX' => 0,
46+
'VND' => 0,
47+
'VUV' => 0,
48+
'XAF' => 0,
49+
'XOF' => 0,
50+
'XPF' => 0,
51+
];
52+
53+
public static $specialActualDecimal = [
54+
'ISK' => 0,
55+
'HUF' => 0,
56+
'TWD' => 0,
57+
'UGX' => 0,
58+
];
59+
60+
// if add more special currencies when it has special actual decimal or special decimal, please also add more test cases
61+
public static $specialCurrenciesMaximumDigits = [
62+
'IDR' => 9,
63+
'INR' => 9,
64+
];
65+
66+
private static function getCurrency()
67+
{
68+
$currency = strtoupper(config('stripe.currency', 'hkd'));
69+
if (in_array($currency, self::$eurSubCurrencies)) {
70+
$currency = 'EUR';
71+
}
72+
73+
return $currency;
74+
}
75+
76+
public static function getActualDecimal(): int
77+
{
78+
$currency = self::getCurrency();
79+
if (array_key_exists($currency, self::$specialDecimalCurrencies)) {
80+
return self::$specialDecimalCurrencies[$currency];
81+
}
82+
if (in_array($currency, self::$supportCurrencies)) {
83+
return 2;
84+
}
85+
86+
throw new UnsupportedCurrency($currency);
87+
}
88+
89+
public static function getValidationDecimal(): int
90+
{
91+
$currency = self::getCurrency();
92+
if (array_key_exists($currency, self::$specialActualDecimal)) {
93+
return self::$specialActualDecimal[$currency];
94+
}
95+
96+
return self::getActualDecimal();
97+
}
98+
99+
public static function getMaximumDigits(): int
100+
{
101+
$currency = self::getCurrency();
102+
if (array_key_exists($currency, self::$specialCurrenciesMaximumDigits)) {
103+
return self::$specialCurrenciesMaximumDigits[$currency];
104+
}
105+
if (in_array($currency, self::$supportCurrencies)) {
106+
return 8;
107+
}
108+
109+
throw new UnsupportedCurrency($currency);
110+
}
111+
112+
public static function getMaximumValidation(): int|float
113+
{
114+
$digits = self::getMaximumDigits();
115+
$digits -= self::getActualDecimal();
116+
$decimal = self::getValidationDecimal();
117+
if ($decimal > 0) {
118+
return (float) (str_repeat('9', $digits).'.'.str_repeat('9', $decimal));
119+
} else {
120+
return (int) str_repeat('9', $digits);
121+
}
122+
}
123+
}

app/Library/Stripe/Base.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
class Base
88
{
9-
const STRIPE_VERSION = '2025-04-30';
9+
const STRIPE_VERSION = '2025-04-30.basil';
1010

1111
protected $http;
1212

@@ -15,8 +15,8 @@ class Base
1515
public function __construct()
1616
{
1717
$this->http = Http::baseUrl('https://api.stripe.com/v1')
18-
->withToken(config('service.stripe.keys.secret'))
19-
->withHeader('Stripe-Version', static::STRIPE_VERSION.'.basil');
18+
->withToken(config('stripe.keys.secret'))
19+
->withHeader('Stripe-Version', static::STRIPE_VERSION);
2020
}
2121

2222
public function find(string $id): ?array

app/Library/Stripe/Checkout.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
class Checkout extends Base
66
{
7-
protected $prefix = 'checkouts/sessions';
7+
protected $prefix = 'checkout/sessions';
88

99
public function expire(string $id)
1010
{

app/Library/Stripe/Concerns/Models/Base.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44

55
trait Base
66
{
7-
public ?array $stripe = null;
8-
9-
abstract public function stripeCreate(): array;
7+
public ?array $stripeData = null;
108

119
abstract public function getStripe(): ?array;
1210
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace App\Library\Stripe\Concerns\Models;
4+
5+
trait CreatableBase
6+
{
7+
use Base;
8+
9+
abstract public function stripeCreate(): array;
10+
}

app/Library/Stripe/Concerns/Models/HasStripeCustomer.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
trait HasStripeCustomer
1010
{
11+
use CreatableBase;
12+
1113
public function stripe()
1214
{
1315
return $this->morphOne(StripeCustomer::class, 'customerable');
@@ -25,8 +27,12 @@ public function stripeEmail(): ?string
2527

2628
public function getStripe(): ?array
2729
{
28-
if ($this->stripe) {
29-
return $this->stripe->getStripe();
30+
if ($this->stripeData) {
31+
return $this->stripeData;
32+
} elseif ($this->stripe) {
33+
$this->stripeData = Client::customers()->find($this->stripe->id);
34+
35+
return $this->stripeData;
3036
} else {
3137
$result = Client::customers()->first([
3238
'metadata' => [
@@ -40,7 +46,7 @@ public function getStripe(): ?array
4046
$this->stripeEmail() == $result['email'],
4147
]);
4248
$this->stripe = $this->stripe()->create(['id' => $result['id']]);
43-
$this->stripe->data = $result;
49+
$this->stripeData = $result;
4450
}
4551

4652
return $result;
@@ -52,9 +58,9 @@ public function stripeCreate(): array
5258
if ($this->stripe) {
5359
throw new AlreadyCreatedCustomer($this);
5460
}
55-
$result = $this->getStripe();
56-
if (! $result) {
57-
$result = Client::customers()->create([
61+
$this->stripeData = $this->getStripe();
62+
if (! $this->stripeData) {
63+
$this->stripeData = Client::customers()->create([
5864
'name' => $this->stripeName(),
5965
'email' => $this->stripeEmail(),
6066
'metadata' => [
@@ -63,13 +69,12 @@ public function stripeCreate(): array
6369
],
6470
]);
6571
$this->update([
66-
'synced_to_stripe' => $this->stripeName() == $result['name'] &&
67-
$this->stripeEmail() == $result['email'],
72+
'synced_to_stripe' => $this->stripeName() == $this->stripeData['name'] &&
73+
$this->stripeEmail() == $this->stripeData['email'],
6874
]);
69-
$this->stripe = $this->stripe()->create(['id' => $result['id']]);
75+
$this->stripe = $this->stripe()->create(['id' => $this->stripeData['id']]);
7076
}
71-
$this->stripe->data = $result;
7277

73-
return $result;
78+
return $this->stripeData;
7479
}
7580
}

0 commit comments

Comments
 (0)