@artilingo/artiframe-cli 1.4.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core-stubs/bin/SystemMethod.php +600 -521
- package/core-stubs/bin/ViewMethod.php +591 -393
- package/core-stubs/docs/de.html +4347 -4390
- package/core-stubs/docs/en.html +4347 -4389
- package/core-stubs/docs/es.html +4347 -4390
- package/core-stubs/docs/fr.html +4347 -4389
- package/core-stubs/docs/tr.html +138 -109
- package/package.json +1 -1
- package/src/App.php +8 -0
- package/src/Commands/AddCommand.php +1 -1
- package/src/Commands/ListCommand.php +2 -1
- package/src/Commands/MakeApiCommand.php +1 -1
- package/src/Commands/MakeClassCommand.php +1 -1
- package/src/Commands/MakeViewCommand.php +1 -1
- package/src/Commands/NewProjectCommand.php +37 -25
- package/src/Commands/RemoveCommand.php +21 -10
- package/src/Commands/ServeCommand.php +29 -9
- package/src/Commands/TableCommand.php +2 -1
- package/src/Commands/UpgradeCommand.php +209 -0
- package/src/Commands/VersionCommand.php +2 -1
- package/src/Lang/de.php +7 -1
- package/src/Lang/en.php +6 -0
- package/src/Lang/es.php +7 -1
- package/src/Lang/fr.php +6 -0
- package/src/Lang/tr.php +7 -1
- package/src/Services/Safeguard.php +25 -0
- package/stubs/service/image.stub +334 -334
- package/stubs/service/iyzico.stub +637 -637
- package/stubs/service/jwt.stub +312 -312
- package/stubs/service/phpmailer.stub +143 -143
- package/stubs/service/pusher.stub +232 -232
- package/stubs/service/qrcode.stub +169 -169
- package/stubs/service/redis.stub +361 -361
- package/stubs/service/s3.stub +377 -377
- package/stubs/service/sentry.stub +76 -106
- package/stubs/service/stripe.stub +526 -526
- package/stubs/service/twilio.stub +361 -361
- package/core-stubs/app/R2Manager.php +0 -130
|
@@ -1,526 +1,526 @@
|
|
|
1
|
-
<?php
|
|
2
|
-
|
|
3
|
-
namespace Service;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Stripe Payment Service
|
|
7
|
-
*
|
|
8
|
-
* Provides a clean wrapper around the stripe-php SDK for payment processing,
|
|
9
|
-
* customer management, subscriptions, product creation, and webhook handling.
|
|
10
|
-
*
|
|
11
|
-
* Required environment variables:
|
|
12
|
-
* STRIPE_SECRET_KEY — Your Stripe secret API key
|
|
13
|
-
* STRIPE_PUBLISHABLE_KEY — Your Stripe publishable key (for front-end reference)
|
|
14
|
-
* STRIPE_WEBHOOK_SECRET — Webhook endpoint signing secret
|
|
15
|
-
*
|
|
16
|
-
* @package Service
|
|
17
|
-
*/
|
|
18
|
-
class Stripe
|
|
19
|
-
{
|
|
20
|
-
/**
|
|
21
|
-
* Stripe publishable key (exposed for front-end usage).
|
|
22
|
-
*
|
|
23
|
-
* @var string
|
|
24
|
-
*/
|
|
25
|
-
private string $publishableKey;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Webhook signing secret.
|
|
29
|
-
*
|
|
30
|
-
* @var string
|
|
31
|
-
*/
|
|
32
|
-
private string $webhookSecret;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Create a new Stripe service instance.
|
|
36
|
-
*
|
|
37
|
-
* Sets the API key globally for the Stripe SDK and stores
|
|
38
|
-
* the publishable key and webhook secret for later use.
|
|
39
|
-
*/
|
|
40
|
-
public function __construct()
|
|
41
|
-
{
|
|
42
|
-
\Stripe\Stripe::setApiKey($_ENV['STRIPE_SECRET_KEY']);
|
|
43
|
-
$this->publishableKey = $_ENV['STRIPE_PUBLISHABLE_KEY'];
|
|
44
|
-
$this->webhookSecret = $_ENV['STRIPE_WEBHOOK_SECRET'];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create a PaymentIntent.
|
|
49
|
-
*
|
|
50
|
-
* Use this for the recommended Stripe payment flow where the client
|
|
51
|
-
* confirms the payment on the front-end using the returned client_secret.
|
|
52
|
-
*
|
|
53
|
-
* @param float $amount Amount in major currency units (e.g. 19.99)
|
|
54
|
-
* @param string $currency ISO currency code (default: usd)
|
|
55
|
-
* @param array $metadata Arbitrary key-value metadata
|
|
56
|
-
* @return array Standardised response with clientSecret and paymentIntentId
|
|
57
|
-
*/
|
|
58
|
-
public function createPaymentIntent(float $amount, string $currency = 'usd', array $metadata = []): array
|
|
59
|
-
{
|
|
60
|
-
try {
|
|
61
|
-
$intent = \Stripe\PaymentIntent::create([
|
|
62
|
-
'amount' => (int) round($amount * 100),
|
|
63
|
-
'currency' => strtolower($currency),
|
|
64
|
-
'metadata' => $metadata,
|
|
65
|
-
'automatic_payment_methods' => ['enabled' => true],
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
return [
|
|
69
|
-
'status' => 'success',
|
|
70
|
-
'message' => 'PaymentIntent created.',
|
|
71
|
-
'data' => [
|
|
72
|
-
'paymentIntentId' => $intent->id,
|
|
73
|
-
'clientSecret' => $intent->client_secret,
|
|
74
|
-
'amount' => $intent->amount,
|
|
75
|
-
'currency' => $intent->currency,
|
|
76
|
-
'status' => $intent->status,
|
|
77
|
-
],
|
|
78
|
-
];
|
|
79
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
80
|
-
return [
|
|
81
|
-
'status' => 'error',
|
|
82
|
-
'message' => $e->getMessage(),
|
|
83
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
84
|
-
];
|
|
85
|
-
} catch (\Exception $e) {
|
|
86
|
-
return [
|
|
87
|
-
'status' => 'error',
|
|
88
|
-
'message' => $e->getMessage(),
|
|
89
|
-
'data' => [],
|
|
90
|
-
];
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Confirm an existing PaymentIntent server-side.
|
|
96
|
-
*
|
|
97
|
-
* Typically the client confirms, but this allows server-side confirmation
|
|
98
|
-
* for scenarios like off-session payments.
|
|
99
|
-
*
|
|
100
|
-
* @param string $paymentIntentId The PaymentIntent ID to confirm
|
|
101
|
-
* @return array Standardised response
|
|
102
|
-
*/
|
|
103
|
-
public function confirmPayment(string $paymentIntentId): array
|
|
104
|
-
{
|
|
105
|
-
try {
|
|
106
|
-
$intent = \Stripe\PaymentIntent::retrieve($paymentIntentId);
|
|
107
|
-
$intent->confirm();
|
|
108
|
-
|
|
109
|
-
return [
|
|
110
|
-
'status' => 'success',
|
|
111
|
-
'message' => 'PaymentIntent confirmed.',
|
|
112
|
-
'data' => [
|
|
113
|
-
'paymentIntentId' => $intent->id,
|
|
114
|
-
'status' => $intent->status,
|
|
115
|
-
'amount' => $intent->amount,
|
|
116
|
-
'currency' => $intent->currency,
|
|
117
|
-
],
|
|
118
|
-
];
|
|
119
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
120
|
-
return [
|
|
121
|
-
'status' => 'error',
|
|
122
|
-
'message' => $e->getMessage(),
|
|
123
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
124
|
-
];
|
|
125
|
-
} catch (\Exception $e) {
|
|
126
|
-
return [
|
|
127
|
-
'status' => 'error',
|
|
128
|
-
'message' => $e->getMessage(),
|
|
129
|
-
'data' => [],
|
|
130
|
-
];
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Create a direct charge (legacy flow).
|
|
136
|
-
*
|
|
137
|
-
* Use createPaymentIntent() for new integrations. This method is provided
|
|
138
|
-
* for backward compatibility with token/source-based flows.
|
|
139
|
-
*
|
|
140
|
-
* @param float $amount Amount in major currency units
|
|
141
|
-
* @param string $source Token or source ID (tok_xxx or src_xxx)
|
|
142
|
-
* @param string $currency ISO currency code (default: usd)
|
|
143
|
-
* @param string $description Charge description
|
|
144
|
-
* @return array Standardised response with chargeId
|
|
145
|
-
*/
|
|
146
|
-
public function charge(float $amount, string $source, string $currency = 'usd', string $description = ''): array
|
|
147
|
-
{
|
|
148
|
-
try {
|
|
149
|
-
$charge = \Stripe\Charge::create([
|
|
150
|
-
'amount' => (int) round($amount * 100),
|
|
151
|
-
'currency' => strtolower($currency),
|
|
152
|
-
'source' => $source,
|
|
153
|
-
'description' => $description,
|
|
154
|
-
]);
|
|
155
|
-
|
|
156
|
-
return [
|
|
157
|
-
'status' => 'success',
|
|
158
|
-
'message' => 'Charge created successfully.',
|
|
159
|
-
'data' => [
|
|
160
|
-
'chargeId' => $charge->id,
|
|
161
|
-
'amount' => $charge->amount,
|
|
162
|
-
'currency' => $charge->currency,
|
|
163
|
-
'paid' => $charge->paid,
|
|
164
|
-
'status' => $charge->status,
|
|
165
|
-
'receipt' => $charge->receipt_url,
|
|
166
|
-
],
|
|
167
|
-
];
|
|
168
|
-
} catch (\Stripe\Exception\CardException $e) {
|
|
169
|
-
return [
|
|
170
|
-
'status' => 'error',
|
|
171
|
-
'message' => $e->getMessage(),
|
|
172
|
-
'data' => [
|
|
173
|
-
'stripeCode' => $e->getStripeCode(),
|
|
174
|
-
'declineCode' => $e->getDeclineCode(),
|
|
175
|
-
],
|
|
176
|
-
];
|
|
177
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
178
|
-
return [
|
|
179
|
-
'status' => 'error',
|
|
180
|
-
'message' => $e->getMessage(),
|
|
181
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
182
|
-
];
|
|
183
|
-
} catch (\Exception $e) {
|
|
184
|
-
return [
|
|
185
|
-
'status' => 'error',
|
|
186
|
-
'message' => $e->getMessage(),
|
|
187
|
-
'data' => [],
|
|
188
|
-
];
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Refund a charge.
|
|
194
|
-
*
|
|
195
|
-
* @param string $chargeId The charge ID to refund
|
|
196
|
-
* @param float|null $amount Partial refund amount (null = full refund)
|
|
197
|
-
* @return array Standardised response with refundId
|
|
198
|
-
*/
|
|
199
|
-
public function refund(string $chargeId, float $amount = null): array
|
|
200
|
-
{
|
|
201
|
-
try {
|
|
202
|
-
$params = ['charge' => $chargeId];
|
|
203
|
-
|
|
204
|
-
if ($amount !== null) {
|
|
205
|
-
$params['amount'] = (int) round($amount * 100);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
$refund = \Stripe\Refund::create($params);
|
|
209
|
-
|
|
210
|
-
return [
|
|
211
|
-
'status' => 'success',
|
|
212
|
-
'message' => 'Refund processed successfully.',
|
|
213
|
-
'data' => [
|
|
214
|
-
'refundId' => $refund->id,
|
|
215
|
-
'amount' => $refund->amount,
|
|
216
|
-
'currency' => $refund->currency,
|
|
217
|
-
'status' => $refund->status,
|
|
218
|
-
'chargeId' => $refund->charge,
|
|
219
|
-
],
|
|
220
|
-
];
|
|
221
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
222
|
-
return [
|
|
223
|
-
'status' => 'error',
|
|
224
|
-
'message' => $e->getMessage(),
|
|
225
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
226
|
-
];
|
|
227
|
-
} catch (\Exception $e) {
|
|
228
|
-
return [
|
|
229
|
-
'status' => 'error',
|
|
230
|
-
'message' => $e->getMessage(),
|
|
231
|
-
'data' => [],
|
|
232
|
-
];
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Create a Stripe Customer.
|
|
238
|
-
*
|
|
239
|
-
* Customers are required for subscriptions and saved payment methods.
|
|
240
|
-
*
|
|
241
|
-
* @param string $email Customer e-mail
|
|
242
|
-
* @param string $name Customer full name
|
|
243
|
-
* @param array $metadata Arbitrary key-value metadata
|
|
244
|
-
* @return array Standardised response with customerId
|
|
245
|
-
*/
|
|
246
|
-
public function createCustomer(string $email, string $name = '', array $metadata = []): array
|
|
247
|
-
{
|
|
248
|
-
try {
|
|
249
|
-
$params = [
|
|
250
|
-
'email' => $email,
|
|
251
|
-
'metadata' => $metadata,
|
|
252
|
-
];
|
|
253
|
-
|
|
254
|
-
if ($name !== '') {
|
|
255
|
-
$params['name'] = $name;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
$customer = \Stripe\Customer::create($params);
|
|
259
|
-
|
|
260
|
-
return [
|
|
261
|
-
'status' => 'success',
|
|
262
|
-
'message' => 'Customer created successfully.',
|
|
263
|
-
'data' => [
|
|
264
|
-
'customerId' => $customer->id,
|
|
265
|
-
'email' => $customer->email,
|
|
266
|
-
'name' => $customer->name,
|
|
267
|
-
'created' => date('Y-m-d H:i:s', $customer->created),
|
|
268
|
-
],
|
|
269
|
-
];
|
|
270
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
271
|
-
return [
|
|
272
|
-
'status' => 'error',
|
|
273
|
-
'message' => $e->getMessage(),
|
|
274
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
275
|
-
];
|
|
276
|
-
} catch (\Exception $e) {
|
|
277
|
-
return [
|
|
278
|
-
'status' => 'error',
|
|
279
|
-
'message' => $e->getMessage(),
|
|
280
|
-
'data' => [],
|
|
281
|
-
];
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Create a subscription for a customer.
|
|
287
|
-
*
|
|
288
|
-
* The customer must already have a default payment method attached.
|
|
289
|
-
*
|
|
290
|
-
* @param string $customerId Stripe customer ID
|
|
291
|
-
* @param string $priceId Stripe price ID (price_xxx)
|
|
292
|
-
* @return array Standardised response with subscriptionId
|
|
293
|
-
*/
|
|
294
|
-
public function subscribe(string $customerId, string $priceId): array
|
|
295
|
-
{
|
|
296
|
-
try {
|
|
297
|
-
$subscription = \Stripe\Subscription::create([
|
|
298
|
-
'customer' => $customerId,
|
|
299
|
-
'items' => [['price' => $priceId]],
|
|
300
|
-
'payment_behavior' => 'default_incomplete',
|
|
301
|
-
'expand' => ['latest_invoice.payment_intent'],
|
|
302
|
-
]);
|
|
303
|
-
|
|
304
|
-
$latestInvoice = $subscription->latest_invoice;
|
|
305
|
-
$clientSecret = null;
|
|
306
|
-
|
|
307
|
-
if (is_object($latestInvoice) && $latestInvoice->payment_intent) {
|
|
308
|
-
$clientSecret = $latestInvoice->payment_intent->client_secret;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return [
|
|
312
|
-
'status' => 'success',
|
|
313
|
-
'message' => 'Subscription created.',
|
|
314
|
-
'data' => [
|
|
315
|
-
'subscriptionId' => $subscription->id,
|
|
316
|
-
'status' => $subscription->status,
|
|
317
|
-
'currentPeriodStart' => date('Y-m-d H:i:s', $subscription->current_period_start),
|
|
318
|
-
'currentPeriodEnd' => date('Y-m-d H:i:s', $subscription->current_period_end),
|
|
319
|
-
'clientSecret' => $clientSecret,
|
|
320
|
-
],
|
|
321
|
-
];
|
|
322
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
323
|
-
return [
|
|
324
|
-
'status' => 'error',
|
|
325
|
-
'message' => $e->getMessage(),
|
|
326
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
327
|
-
];
|
|
328
|
-
} catch (\Exception $e) {
|
|
329
|
-
return [
|
|
330
|
-
'status' => 'error',
|
|
331
|
-
'message' => $e->getMessage(),
|
|
332
|
-
'data' => [],
|
|
333
|
-
];
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Cancel a subscription.
|
|
339
|
-
*
|
|
340
|
-
* Cancels immediately. For end-of-period cancellation, modify the
|
|
341
|
-
* subscription's cancel_at_period_end flag instead.
|
|
342
|
-
*
|
|
343
|
-
* @param string $subscriptionId Stripe subscription ID
|
|
344
|
-
* @return array Standardised response
|
|
345
|
-
*/
|
|
346
|
-
public function cancelSubscription(string $subscriptionId): array
|
|
347
|
-
{
|
|
348
|
-
try {
|
|
349
|
-
$subscription = \Stripe\Subscription::retrieve($subscriptionId);
|
|
350
|
-
$subscription->cancel();
|
|
351
|
-
|
|
352
|
-
return [
|
|
353
|
-
'status' => 'success',
|
|
354
|
-
'message' => 'Subscription cancelled.',
|
|
355
|
-
'data' => [
|
|
356
|
-
'subscriptionId' => $subscription->id,
|
|
357
|
-
'status' => $subscription->status,
|
|
358
|
-
'canceledAt' => $subscription->canceled_at
|
|
359
|
-
? date('Y-m-d H:i:s', $subscription->canceled_at)
|
|
360
|
-
: null,
|
|
361
|
-
],
|
|
362
|
-
];
|
|
363
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
364
|
-
return [
|
|
365
|
-
'status' => 'error',
|
|
366
|
-
'message' => $e->getMessage(),
|
|
367
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
368
|
-
];
|
|
369
|
-
} catch (\Exception $e) {
|
|
370
|
-
return [
|
|
371
|
-
'status' => 'error',
|
|
372
|
-
'message' => $e->getMessage(),
|
|
373
|
-
'data' => [],
|
|
374
|
-
];
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Create a Product and its associated Price in one call.
|
|
380
|
-
*
|
|
381
|
-
* Simplifies the two-step Stripe flow of creating a product first,
|
|
382
|
-
* then attaching a recurring or one-time price to it.
|
|
383
|
-
*
|
|
384
|
-
* @param string $name Product name
|
|
385
|
-
* @param float $price Unit price in major currency units
|
|
386
|
-
* @param string $currency ISO currency code (default: usd)
|
|
387
|
-
* @return array Standardised response with productId and priceId
|
|
388
|
-
*/
|
|
389
|
-
public function createProduct(string $name, float $price, string $currency = 'usd'): array
|
|
390
|
-
{
|
|
391
|
-
try {
|
|
392
|
-
$product = \Stripe\Product::create([
|
|
393
|
-
'name' => $name,
|
|
394
|
-
]);
|
|
395
|
-
|
|
396
|
-
$priceObj = \Stripe\Price::create([
|
|
397
|
-
'product' => $product->id,
|
|
398
|
-
'unit_amount' => (int) round($price * 100),
|
|
399
|
-
'currency' => strtolower($currency),
|
|
400
|
-
]);
|
|
401
|
-
|
|
402
|
-
return [
|
|
403
|
-
'status' => 'success',
|
|
404
|
-
'message' => 'Product and price created.',
|
|
405
|
-
'data' => [
|
|
406
|
-
'productId' => $product->id,
|
|
407
|
-
'priceId' => $priceObj->id,
|
|
408
|
-
'name' => $product->name,
|
|
409
|
-
'amount' => $priceObj->unit_amount,
|
|
410
|
-
'currency' => $priceObj->currency,
|
|
411
|
-
],
|
|
412
|
-
];
|
|
413
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
414
|
-
return [
|
|
415
|
-
'status' => 'error',
|
|
416
|
-
'message' => $e->getMessage(),
|
|
417
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
418
|
-
];
|
|
419
|
-
} catch (\Exception $e) {
|
|
420
|
-
return [
|
|
421
|
-
'status' => 'error',
|
|
422
|
-
'message' => $e->getMessage(),
|
|
423
|
-
'data' => [],
|
|
424
|
-
];
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Verify and parse a Stripe webhook event.
|
|
430
|
-
*
|
|
431
|
-
* Validates the webhook signature using the signing secret and returns
|
|
432
|
-
* the parsed event data. Call this from your webhook endpoint handler.
|
|
433
|
-
*
|
|
434
|
-
* @param string $payload Raw request body (file_get_contents('php://input'))
|
|
435
|
-
* @param string $signature Stripe-Signature header value
|
|
436
|
-
* @return array Standardised response with event type and data
|
|
437
|
-
*/
|
|
438
|
-
public function webhook(string $payload, string $signature): array
|
|
439
|
-
{
|
|
440
|
-
try {
|
|
441
|
-
$event = \Stripe\Webhook::constructEvent(
|
|
442
|
-
$payload,
|
|
443
|
-
$signature,
|
|
444
|
-
$this->webhookSecret
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
return [
|
|
448
|
-
'status' => 'success',
|
|
449
|
-
'message' => 'Webhook verified.',
|
|
450
|
-
'data' => [
|
|
451
|
-
'eventId' => $event->id,
|
|
452
|
-
'eventType' => $event->type,
|
|
453
|
-
'created' => date('Y-m-d H:i:s', $event->created),
|
|
454
|
-
'object' => $event->data->object->toArray(),
|
|
455
|
-
],
|
|
456
|
-
];
|
|
457
|
-
} catch (\Stripe\Exception\SignatureVerificationException $e) {
|
|
458
|
-
return [
|
|
459
|
-
'status' => 'error',
|
|
460
|
-
'message' => 'Webhook signature verification failed: ' . $e->getMessage(),
|
|
461
|
-
'data' => [],
|
|
462
|
-
];
|
|
463
|
-
} catch (\UnexpectedValueException $e) {
|
|
464
|
-
return [
|
|
465
|
-
'status' => 'error',
|
|
466
|
-
'message' => 'Invalid webhook payload: ' . $e->getMessage(),
|
|
467
|
-
'data' => [],
|
|
468
|
-
];
|
|
469
|
-
} catch (\Exception $e) {
|
|
470
|
-
return [
|
|
471
|
-
'status' => 'error',
|
|
472
|
-
'message' => $e->getMessage(),
|
|
473
|
-
'data' => [],
|
|
474
|
-
];
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* List recent PaymentIntents.
|
|
480
|
-
*
|
|
481
|
-
* @param int $limit Number of results to return (max 100, default 10)
|
|
482
|
-
* @return array Standardised response with array of payment intents
|
|
483
|
-
*/
|
|
484
|
-
public function listPayments(int $limit = 10): array
|
|
485
|
-
{
|
|
486
|
-
try {
|
|
487
|
-
$intents = \Stripe\PaymentIntent::all([
|
|
488
|
-
'limit' => min($limit, 100),
|
|
489
|
-
]);
|
|
490
|
-
|
|
491
|
-
$payments = [];
|
|
492
|
-
foreach ($intents->data as $intent) {
|
|
493
|
-
$payments[] = [
|
|
494
|
-
'paymentIntentId' => $intent->id,
|
|
495
|
-
'amount' => $intent->amount,
|
|
496
|
-
'currency' => $intent->currency,
|
|
497
|
-
'status' => $intent->status,
|
|
498
|
-
'created' => date('Y-m-d H:i:s', $intent->created),
|
|
499
|
-
'description' => $intent->description,
|
|
500
|
-
'metadata' => $intent->metadata->toArray(),
|
|
501
|
-
];
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
return [
|
|
505
|
-
'status' => 'success',
|
|
506
|
-
'message' => count($payments) . ' payment(s) retrieved.',
|
|
507
|
-
'data' => [
|
|
508
|
-
'payments' => $payments,
|
|
509
|
-
'hasMore' => $intents->has_more,
|
|
510
|
-
],
|
|
511
|
-
];
|
|
512
|
-
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
513
|
-
return [
|
|
514
|
-
'status' => 'error',
|
|
515
|
-
'message' => $e->getMessage(),
|
|
516
|
-
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
517
|
-
];
|
|
518
|
-
} catch (\Exception $e) {
|
|
519
|
-
return [
|
|
520
|
-
'status' => 'error',
|
|
521
|
-
'message' => $e->getMessage(),
|
|
522
|
-
'data' => [],
|
|
523
|
-
];
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
}
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
namespace Src\Service;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Stripe Payment Service
|
|
7
|
+
*
|
|
8
|
+
* Provides a clean wrapper around the stripe-php SDK for payment processing,
|
|
9
|
+
* customer management, subscriptions, product creation, and webhook handling.
|
|
10
|
+
*
|
|
11
|
+
* Required environment variables:
|
|
12
|
+
* STRIPE_SECRET_KEY — Your Stripe secret API key
|
|
13
|
+
* STRIPE_PUBLISHABLE_KEY — Your Stripe publishable key (for front-end reference)
|
|
14
|
+
* STRIPE_WEBHOOK_SECRET — Webhook endpoint signing secret
|
|
15
|
+
*
|
|
16
|
+
* @package Service
|
|
17
|
+
*/
|
|
18
|
+
class Stripe
|
|
19
|
+
{
|
|
20
|
+
/**
|
|
21
|
+
* Stripe publishable key (exposed for front-end usage).
|
|
22
|
+
*
|
|
23
|
+
* @var string
|
|
24
|
+
*/
|
|
25
|
+
private string $publishableKey;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Webhook signing secret.
|
|
29
|
+
*
|
|
30
|
+
* @var string
|
|
31
|
+
*/
|
|
32
|
+
private string $webhookSecret;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a new Stripe service instance.
|
|
36
|
+
*
|
|
37
|
+
* Sets the API key globally for the Stripe SDK and stores
|
|
38
|
+
* the publishable key and webhook secret for later use.
|
|
39
|
+
*/
|
|
40
|
+
public function __construct()
|
|
41
|
+
{
|
|
42
|
+
\Stripe\Stripe::setApiKey($_ENV['STRIPE_SECRET_KEY']);
|
|
43
|
+
$this->publishableKey = $_ENV['STRIPE_PUBLISHABLE_KEY'];
|
|
44
|
+
$this->webhookSecret = $_ENV['STRIPE_WEBHOOK_SECRET'];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Create a PaymentIntent.
|
|
49
|
+
*
|
|
50
|
+
* Use this for the recommended Stripe payment flow where the client
|
|
51
|
+
* confirms the payment on the front-end using the returned client_secret.
|
|
52
|
+
*
|
|
53
|
+
* @param float $amount Amount in major currency units (e.g. 19.99)
|
|
54
|
+
* @param string $currency ISO currency code (default: usd)
|
|
55
|
+
* @param array $metadata Arbitrary key-value metadata
|
|
56
|
+
* @return array Standardised response with clientSecret and paymentIntentId
|
|
57
|
+
*/
|
|
58
|
+
public function createPaymentIntent(float $amount, string $currency = 'usd', array $metadata = []): array
|
|
59
|
+
{
|
|
60
|
+
try {
|
|
61
|
+
$intent = \Stripe\PaymentIntent::create([
|
|
62
|
+
'amount' => (int) round($amount * 100),
|
|
63
|
+
'currency' => strtolower($currency),
|
|
64
|
+
'metadata' => $metadata,
|
|
65
|
+
'automatic_payment_methods' => ['enabled' => true],
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
return [
|
|
69
|
+
'status' => 'success',
|
|
70
|
+
'message' => 'PaymentIntent created.',
|
|
71
|
+
'data' => [
|
|
72
|
+
'paymentIntentId' => $intent->id,
|
|
73
|
+
'clientSecret' => $intent->client_secret,
|
|
74
|
+
'amount' => $intent->amount,
|
|
75
|
+
'currency' => $intent->currency,
|
|
76
|
+
'status' => $intent->status,
|
|
77
|
+
],
|
|
78
|
+
];
|
|
79
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
80
|
+
return [
|
|
81
|
+
'status' => 'error',
|
|
82
|
+
'message' => $e->getMessage(),
|
|
83
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
84
|
+
];
|
|
85
|
+
} catch (\Exception $e) {
|
|
86
|
+
return [
|
|
87
|
+
'status' => 'error',
|
|
88
|
+
'message' => $e->getMessage(),
|
|
89
|
+
'data' => [],
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Confirm an existing PaymentIntent server-side.
|
|
96
|
+
*
|
|
97
|
+
* Typically the client confirms, but this allows server-side confirmation
|
|
98
|
+
* for scenarios like off-session payments.
|
|
99
|
+
*
|
|
100
|
+
* @param string $paymentIntentId The PaymentIntent ID to confirm
|
|
101
|
+
* @return array Standardised response
|
|
102
|
+
*/
|
|
103
|
+
public function confirmPayment(string $paymentIntentId): array
|
|
104
|
+
{
|
|
105
|
+
try {
|
|
106
|
+
$intent = \Stripe\PaymentIntent::retrieve($paymentIntentId);
|
|
107
|
+
$intent->confirm();
|
|
108
|
+
|
|
109
|
+
return [
|
|
110
|
+
'status' => 'success',
|
|
111
|
+
'message' => 'PaymentIntent confirmed.',
|
|
112
|
+
'data' => [
|
|
113
|
+
'paymentIntentId' => $intent->id,
|
|
114
|
+
'status' => $intent->status,
|
|
115
|
+
'amount' => $intent->amount,
|
|
116
|
+
'currency' => $intent->currency,
|
|
117
|
+
],
|
|
118
|
+
];
|
|
119
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
120
|
+
return [
|
|
121
|
+
'status' => 'error',
|
|
122
|
+
'message' => $e->getMessage(),
|
|
123
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
124
|
+
];
|
|
125
|
+
} catch (\Exception $e) {
|
|
126
|
+
return [
|
|
127
|
+
'status' => 'error',
|
|
128
|
+
'message' => $e->getMessage(),
|
|
129
|
+
'data' => [],
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create a direct charge (legacy flow).
|
|
136
|
+
*
|
|
137
|
+
* Use createPaymentIntent() for new integrations. This method is provided
|
|
138
|
+
* for backward compatibility with token/source-based flows.
|
|
139
|
+
*
|
|
140
|
+
* @param float $amount Amount in major currency units
|
|
141
|
+
* @param string $source Token or source ID (tok_xxx or src_xxx)
|
|
142
|
+
* @param string $currency ISO currency code (default: usd)
|
|
143
|
+
* @param string $description Charge description
|
|
144
|
+
* @return array Standardised response with chargeId
|
|
145
|
+
*/
|
|
146
|
+
public function charge(float $amount, string $source, string $currency = 'usd', string $description = ''): array
|
|
147
|
+
{
|
|
148
|
+
try {
|
|
149
|
+
$charge = \Stripe\Charge::create([
|
|
150
|
+
'amount' => (int) round($amount * 100),
|
|
151
|
+
'currency' => strtolower($currency),
|
|
152
|
+
'source' => $source,
|
|
153
|
+
'description' => $description,
|
|
154
|
+
]);
|
|
155
|
+
|
|
156
|
+
return [
|
|
157
|
+
'status' => 'success',
|
|
158
|
+
'message' => 'Charge created successfully.',
|
|
159
|
+
'data' => [
|
|
160
|
+
'chargeId' => $charge->id,
|
|
161
|
+
'amount' => $charge->amount,
|
|
162
|
+
'currency' => $charge->currency,
|
|
163
|
+
'paid' => $charge->paid,
|
|
164
|
+
'status' => $charge->status,
|
|
165
|
+
'receipt' => $charge->receipt_url,
|
|
166
|
+
],
|
|
167
|
+
];
|
|
168
|
+
} catch (\Stripe\Exception\CardException $e) {
|
|
169
|
+
return [
|
|
170
|
+
'status' => 'error',
|
|
171
|
+
'message' => $e->getMessage(),
|
|
172
|
+
'data' => [
|
|
173
|
+
'stripeCode' => $e->getStripeCode(),
|
|
174
|
+
'declineCode' => $e->getDeclineCode(),
|
|
175
|
+
],
|
|
176
|
+
];
|
|
177
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
178
|
+
return [
|
|
179
|
+
'status' => 'error',
|
|
180
|
+
'message' => $e->getMessage(),
|
|
181
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
182
|
+
];
|
|
183
|
+
} catch (\Exception $e) {
|
|
184
|
+
return [
|
|
185
|
+
'status' => 'error',
|
|
186
|
+
'message' => $e->getMessage(),
|
|
187
|
+
'data' => [],
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Refund a charge.
|
|
194
|
+
*
|
|
195
|
+
* @param string $chargeId The charge ID to refund
|
|
196
|
+
* @param float|null $amount Partial refund amount (null = full refund)
|
|
197
|
+
* @return array Standardised response with refundId
|
|
198
|
+
*/
|
|
199
|
+
public function refund(string $chargeId, float $amount = null): array
|
|
200
|
+
{
|
|
201
|
+
try {
|
|
202
|
+
$params = ['charge' => $chargeId];
|
|
203
|
+
|
|
204
|
+
if ($amount !== null) {
|
|
205
|
+
$params['amount'] = (int) round($amount * 100);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
$refund = \Stripe\Refund::create($params);
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
'status' => 'success',
|
|
212
|
+
'message' => 'Refund processed successfully.',
|
|
213
|
+
'data' => [
|
|
214
|
+
'refundId' => $refund->id,
|
|
215
|
+
'amount' => $refund->amount,
|
|
216
|
+
'currency' => $refund->currency,
|
|
217
|
+
'status' => $refund->status,
|
|
218
|
+
'chargeId' => $refund->charge,
|
|
219
|
+
],
|
|
220
|
+
];
|
|
221
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
222
|
+
return [
|
|
223
|
+
'status' => 'error',
|
|
224
|
+
'message' => $e->getMessage(),
|
|
225
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
226
|
+
];
|
|
227
|
+
} catch (\Exception $e) {
|
|
228
|
+
return [
|
|
229
|
+
'status' => 'error',
|
|
230
|
+
'message' => $e->getMessage(),
|
|
231
|
+
'data' => [],
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create a Stripe Customer.
|
|
238
|
+
*
|
|
239
|
+
* Customers are required for subscriptions and saved payment methods.
|
|
240
|
+
*
|
|
241
|
+
* @param string $email Customer e-mail
|
|
242
|
+
* @param string $name Customer full name
|
|
243
|
+
* @param array $metadata Arbitrary key-value metadata
|
|
244
|
+
* @return array Standardised response with customerId
|
|
245
|
+
*/
|
|
246
|
+
public function createCustomer(string $email, string $name = '', array $metadata = []): array
|
|
247
|
+
{
|
|
248
|
+
try {
|
|
249
|
+
$params = [
|
|
250
|
+
'email' => $email,
|
|
251
|
+
'metadata' => $metadata,
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
if ($name !== '') {
|
|
255
|
+
$params['name'] = $name;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
$customer = \Stripe\Customer::create($params);
|
|
259
|
+
|
|
260
|
+
return [
|
|
261
|
+
'status' => 'success',
|
|
262
|
+
'message' => 'Customer created successfully.',
|
|
263
|
+
'data' => [
|
|
264
|
+
'customerId' => $customer->id,
|
|
265
|
+
'email' => $customer->email,
|
|
266
|
+
'name' => $customer->name,
|
|
267
|
+
'created' => date('Y-m-d H:i:s', $customer->created),
|
|
268
|
+
],
|
|
269
|
+
];
|
|
270
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
271
|
+
return [
|
|
272
|
+
'status' => 'error',
|
|
273
|
+
'message' => $e->getMessage(),
|
|
274
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
275
|
+
];
|
|
276
|
+
} catch (\Exception $e) {
|
|
277
|
+
return [
|
|
278
|
+
'status' => 'error',
|
|
279
|
+
'message' => $e->getMessage(),
|
|
280
|
+
'data' => [],
|
|
281
|
+
];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Create a subscription for a customer.
|
|
287
|
+
*
|
|
288
|
+
* The customer must already have a default payment method attached.
|
|
289
|
+
*
|
|
290
|
+
* @param string $customerId Stripe customer ID
|
|
291
|
+
* @param string $priceId Stripe price ID (price_xxx)
|
|
292
|
+
* @return array Standardised response with subscriptionId
|
|
293
|
+
*/
|
|
294
|
+
public function subscribe(string $customerId, string $priceId): array
|
|
295
|
+
{
|
|
296
|
+
try {
|
|
297
|
+
$subscription = \Stripe\Subscription::create([
|
|
298
|
+
'customer' => $customerId,
|
|
299
|
+
'items' => [['price' => $priceId]],
|
|
300
|
+
'payment_behavior' => 'default_incomplete',
|
|
301
|
+
'expand' => ['latest_invoice.payment_intent'],
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
$latestInvoice = $subscription->latest_invoice;
|
|
305
|
+
$clientSecret = null;
|
|
306
|
+
|
|
307
|
+
if (is_object($latestInvoice) && $latestInvoice->payment_intent) {
|
|
308
|
+
$clientSecret = $latestInvoice->payment_intent->client_secret;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return [
|
|
312
|
+
'status' => 'success',
|
|
313
|
+
'message' => 'Subscription created.',
|
|
314
|
+
'data' => [
|
|
315
|
+
'subscriptionId' => $subscription->id,
|
|
316
|
+
'status' => $subscription->status,
|
|
317
|
+
'currentPeriodStart' => date('Y-m-d H:i:s', $subscription->current_period_start),
|
|
318
|
+
'currentPeriodEnd' => date('Y-m-d H:i:s', $subscription->current_period_end),
|
|
319
|
+
'clientSecret' => $clientSecret,
|
|
320
|
+
],
|
|
321
|
+
];
|
|
322
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
323
|
+
return [
|
|
324
|
+
'status' => 'error',
|
|
325
|
+
'message' => $e->getMessage(),
|
|
326
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
327
|
+
];
|
|
328
|
+
} catch (\Exception $e) {
|
|
329
|
+
return [
|
|
330
|
+
'status' => 'error',
|
|
331
|
+
'message' => $e->getMessage(),
|
|
332
|
+
'data' => [],
|
|
333
|
+
];
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Cancel a subscription.
|
|
339
|
+
*
|
|
340
|
+
* Cancels immediately. For end-of-period cancellation, modify the
|
|
341
|
+
* subscription's cancel_at_period_end flag instead.
|
|
342
|
+
*
|
|
343
|
+
* @param string $subscriptionId Stripe subscription ID
|
|
344
|
+
* @return array Standardised response
|
|
345
|
+
*/
|
|
346
|
+
public function cancelSubscription(string $subscriptionId): array
|
|
347
|
+
{
|
|
348
|
+
try {
|
|
349
|
+
$subscription = \Stripe\Subscription::retrieve($subscriptionId);
|
|
350
|
+
$subscription->cancel();
|
|
351
|
+
|
|
352
|
+
return [
|
|
353
|
+
'status' => 'success',
|
|
354
|
+
'message' => 'Subscription cancelled.',
|
|
355
|
+
'data' => [
|
|
356
|
+
'subscriptionId' => $subscription->id,
|
|
357
|
+
'status' => $subscription->status,
|
|
358
|
+
'canceledAt' => $subscription->canceled_at
|
|
359
|
+
? date('Y-m-d H:i:s', $subscription->canceled_at)
|
|
360
|
+
: null,
|
|
361
|
+
],
|
|
362
|
+
];
|
|
363
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
364
|
+
return [
|
|
365
|
+
'status' => 'error',
|
|
366
|
+
'message' => $e->getMessage(),
|
|
367
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
368
|
+
];
|
|
369
|
+
} catch (\Exception $e) {
|
|
370
|
+
return [
|
|
371
|
+
'status' => 'error',
|
|
372
|
+
'message' => $e->getMessage(),
|
|
373
|
+
'data' => [],
|
|
374
|
+
];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Create a Product and its associated Price in one call.
|
|
380
|
+
*
|
|
381
|
+
* Simplifies the two-step Stripe flow of creating a product first,
|
|
382
|
+
* then attaching a recurring or one-time price to it.
|
|
383
|
+
*
|
|
384
|
+
* @param string $name Product name
|
|
385
|
+
* @param float $price Unit price in major currency units
|
|
386
|
+
* @param string $currency ISO currency code (default: usd)
|
|
387
|
+
* @return array Standardised response with productId and priceId
|
|
388
|
+
*/
|
|
389
|
+
public function createProduct(string $name, float $price, string $currency = 'usd'): array
|
|
390
|
+
{
|
|
391
|
+
try {
|
|
392
|
+
$product = \Stripe\Product::create([
|
|
393
|
+
'name' => $name,
|
|
394
|
+
]);
|
|
395
|
+
|
|
396
|
+
$priceObj = \Stripe\Price::create([
|
|
397
|
+
'product' => $product->id,
|
|
398
|
+
'unit_amount' => (int) round($price * 100),
|
|
399
|
+
'currency' => strtolower($currency),
|
|
400
|
+
]);
|
|
401
|
+
|
|
402
|
+
return [
|
|
403
|
+
'status' => 'success',
|
|
404
|
+
'message' => 'Product and price created.',
|
|
405
|
+
'data' => [
|
|
406
|
+
'productId' => $product->id,
|
|
407
|
+
'priceId' => $priceObj->id,
|
|
408
|
+
'name' => $product->name,
|
|
409
|
+
'amount' => $priceObj->unit_amount,
|
|
410
|
+
'currency' => $priceObj->currency,
|
|
411
|
+
],
|
|
412
|
+
];
|
|
413
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
414
|
+
return [
|
|
415
|
+
'status' => 'error',
|
|
416
|
+
'message' => $e->getMessage(),
|
|
417
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
418
|
+
];
|
|
419
|
+
} catch (\Exception $e) {
|
|
420
|
+
return [
|
|
421
|
+
'status' => 'error',
|
|
422
|
+
'message' => $e->getMessage(),
|
|
423
|
+
'data' => [],
|
|
424
|
+
];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Verify and parse a Stripe webhook event.
|
|
430
|
+
*
|
|
431
|
+
* Validates the webhook signature using the signing secret and returns
|
|
432
|
+
* the parsed event data. Call this from your webhook endpoint handler.
|
|
433
|
+
*
|
|
434
|
+
* @param string $payload Raw request body (file_get_contents('php://input'))
|
|
435
|
+
* @param string $signature Stripe-Signature header value
|
|
436
|
+
* @return array Standardised response with event type and data
|
|
437
|
+
*/
|
|
438
|
+
public function webhook(string $payload, string $signature): array
|
|
439
|
+
{
|
|
440
|
+
try {
|
|
441
|
+
$event = \Stripe\Webhook::constructEvent(
|
|
442
|
+
$payload,
|
|
443
|
+
$signature,
|
|
444
|
+
$this->webhookSecret
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
return [
|
|
448
|
+
'status' => 'success',
|
|
449
|
+
'message' => 'Webhook verified.',
|
|
450
|
+
'data' => [
|
|
451
|
+
'eventId' => $event->id,
|
|
452
|
+
'eventType' => $event->type,
|
|
453
|
+
'created' => date('Y-m-d H:i:s', $event->created),
|
|
454
|
+
'object' => $event->data->object->toArray(),
|
|
455
|
+
],
|
|
456
|
+
];
|
|
457
|
+
} catch (\Stripe\Exception\SignatureVerificationException $e) {
|
|
458
|
+
return [
|
|
459
|
+
'status' => 'error',
|
|
460
|
+
'message' => 'Webhook signature verification failed: ' . $e->getMessage(),
|
|
461
|
+
'data' => [],
|
|
462
|
+
];
|
|
463
|
+
} catch (\UnexpectedValueException $e) {
|
|
464
|
+
return [
|
|
465
|
+
'status' => 'error',
|
|
466
|
+
'message' => 'Invalid webhook payload: ' . $e->getMessage(),
|
|
467
|
+
'data' => [],
|
|
468
|
+
];
|
|
469
|
+
} catch (\Exception $e) {
|
|
470
|
+
return [
|
|
471
|
+
'status' => 'error',
|
|
472
|
+
'message' => $e->getMessage(),
|
|
473
|
+
'data' => [],
|
|
474
|
+
];
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* List recent PaymentIntents.
|
|
480
|
+
*
|
|
481
|
+
* @param int $limit Number of results to return (max 100, default 10)
|
|
482
|
+
* @return array Standardised response with array of payment intents
|
|
483
|
+
*/
|
|
484
|
+
public function listPayments(int $limit = 10): array
|
|
485
|
+
{
|
|
486
|
+
try {
|
|
487
|
+
$intents = \Stripe\PaymentIntent::all([
|
|
488
|
+
'limit' => min($limit, 100),
|
|
489
|
+
]);
|
|
490
|
+
|
|
491
|
+
$payments = [];
|
|
492
|
+
foreach ($intents->data as $intent) {
|
|
493
|
+
$payments[] = [
|
|
494
|
+
'paymentIntentId' => $intent->id,
|
|
495
|
+
'amount' => $intent->amount,
|
|
496
|
+
'currency' => $intent->currency,
|
|
497
|
+
'status' => $intent->status,
|
|
498
|
+
'created' => date('Y-m-d H:i:s', $intent->created),
|
|
499
|
+
'description' => $intent->description,
|
|
500
|
+
'metadata' => $intent->metadata->toArray(),
|
|
501
|
+
];
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
return [
|
|
505
|
+
'status' => 'success',
|
|
506
|
+
'message' => count($payments) . ' payment(s) retrieved.',
|
|
507
|
+
'data' => [
|
|
508
|
+
'payments' => $payments,
|
|
509
|
+
'hasMore' => $intents->has_more,
|
|
510
|
+
],
|
|
511
|
+
];
|
|
512
|
+
} catch (\Stripe\Exception\ApiErrorException $e) {
|
|
513
|
+
return [
|
|
514
|
+
'status' => 'error',
|
|
515
|
+
'message' => $e->getMessage(),
|
|
516
|
+
'data' => ['stripeCode' => $e->getStripeCode()],
|
|
517
|
+
];
|
|
518
|
+
} catch (\Exception $e) {
|
|
519
|
+
return [
|
|
520
|
+
'status' => 'error',
|
|
521
|
+
'message' => $e->getMessage(),
|
|
522
|
+
'data' => [],
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|