Skip to content

Commit 29e60aa

Browse files
committed
Version 1.1.0
1 parent 7a226ce commit 29e60aa

20 files changed

+383
-101
lines changed

README.md

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,22 @@ configuration in container-less case).
1616
# Taking a part in this project
1717

1818
The task "to cover all the functionality of `stripe-php` library" is really huge. It's possible to achieve it,
19-
but it requires a huge efforts (as huge as the task is) and is financially senseless in order to be implemented
20-
by a group of programmers while working on the main project.
19+
but it requires a huge efforts (as huge as the task is) and is not always possible while you are working on the
20+
main project.
2121

2222
Therefore, it is **highly** appreciated to take part in this project for anyone interested in any of thees ways:
2323
- You have an idea on how to improve this project, or you see that something is wrong? Don't hesitate opening
2424
an issue.
2525
- You have time and will to add/improve functionality or fix a bug? Your pull requests would be **extremely**
2626
valuable
2727

28+
If you cover just a part of Stripe's API you're using, it is possible that one day the whole API will be covered.
29+
2830
# Installation
2931

30-
Nothing special here, just use composer to install the package and that's it.
32+
Nothing special here, just use composer to install the package:
33+
34+
> composer install readdle/stripe-httpclient-mock
3135
3236
# Usage
3337

@@ -44,25 +48,25 @@ piece of code instead of performing real HTTP requests.
4448

4549
### Files
4650

47-
`Collection.php` - representation of collection of entities
51+
`src/Collection.php` - representation of collection of entities
4852

49-
`EntityManager.php` - manager, which is responsible for creating/updating/deleting/listing entities, also performs
53+
`src/EntityManager.php` - manager, which is responsible for creating/updating/deleting/listing entities, also performs
5054
search and paging functions
5155

52-
`HttpClient.php` - it's obvious from its name, HTTP client which substitutes `stripe-php`'s curl-based client
56+
`src/HttpClient.php` - HTTP client which substitutes `stripe-php`'s curl-based client
5357

5458
### Directories
5559

56-
`Entity` - implemented entities, each entity **must** extend AbstractEntity class
60+
`src/Entity` - implemented entities, each entity **must** extend AbstractEntity class
5761

58-
`Error` - erroneous responses, each error **must** extend AbstractError class
62+
`src/Error` - erroneous responses, each error **must** extend AbstractError class
5963

60-
`Success` - success responses, which doesn't contain entity in it, but an information about an action
64+
`src/Success` - success responses, which doesn't contain entity in it, but an information about an action
6165
and its result, **must** implement `ResponseInterface`
6266

63-
# Entity structure
67+
# Entity class structure
6468

65-
### Props
69+
### Properties
6670

6771
Each entity **must** have at least `$props` property filled in with all the fields this entity has (the list of fields
6872
for each entity could be found in [Stripe's documentation](https://stripe.com/docs/api)).

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "Mock of Stripe's HttpClient which can be used in testing purposes in order to test your code and not to perform actual HTTP requests",
44
"type": "library",
55
"license": "MIT",
6-
"version": "1.0.0",
6+
"version": "1.1.0",
77
"php": ">=7.4",
88
"autoload": {
99
"psr-4": {

src/Collection.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
use Readdle\StripeHttpClientMock\Entity\AbstractEntity;
77

8-
class Collection
8+
class Collection implements ResponseInterface
99
{
1010
public string $object = 'list';
1111
public array $data = [];
@@ -19,11 +19,16 @@ public function __construct(array $data = [], bool $hasMore = false, string $url
1919
$this->url = $url;
2020
}
2121

22-
public function add(AbstractEntity $entity)
22+
public function add(AbstractEntity $entity): void
2323
{
2424
$this->data[] = $entity;
2525
}
2626

27+
public function addCollection(Collection $collection): void
28+
{
29+
$this->data = array_merge($this->data, $collection->data);
30+
}
31+
2732
public function toArray(): array
2833
{
2934
return [
@@ -38,4 +43,9 @@ public function toString(): string
3843
{
3944
return json_encode($this->toArray());
4045
}
46+
47+
public function getHttpStatusCode(): int
48+
{
49+
return 200;
50+
}
4151
}

src/Entity/AbstractEntity.php

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function __set(string $key, $value): void
2525
return;
2626
}
2727

28-
$this->props[$key] = $value;
28+
$this->fillProps([$key => $value]);
2929
}
3030

3131
public static function create(string $id, array $props = []): ResponseInterface
@@ -36,25 +36,53 @@ public static function create(string $id, array $props = []): ResponseInterface
3636
$shortClass = substr($class, strrpos($class, '\\') + 1);
3737
$object = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $shortClass));
3838

39-
$entity->props['id'] = $id;
40-
$entity->props['object'] = $object;
41-
42-
$entity->update($props);
39+
$mixedProps = array_merge(
40+
[
41+
'id' => $id,
42+
'object' => $object,
43+
],
44+
$entity->props,
45+
$props
46+
);
47+
48+
if (array_key_exists('created', $mixedProps) && empty($mixedProps['created'])) {
49+
$mixedProps['created'] = time();
50+
}
4351

52+
$entity->props = [];
53+
$entity->fillProps($mixedProps);
4454
return $entity;
4555
}
4656

47-
public function update(array $props): void
57+
protected function fillProps(array $props): void
4858
{
59+
$sampleProps = (new static())->props;
60+
$sampleProps['id'] = '';
61+
$sampleProps['object'] = '';
62+
4963
foreach ($props as $key => $value) {
50-
if (is_bool($this->$key) && is_string($value)) {
64+
if (!array_key_exists($key, $sampleProps)) {
65+
continue;
66+
}
67+
68+
if (is_bool($sampleProps[$key]) && is_string($value)) {
5169
$value = $value !== 'false';
5270
}
5371

54-
$this->$key = $value;
72+
if ($value === '') {
73+
$value = null;
74+
}
75+
76+
$this->props[$key] = $value;
5577
}
5678
}
5779

80+
public function update(array $props): ResponseInterface
81+
{
82+
$this->fillProps($props);
83+
return $this;
84+
}
85+
5886
/**
5987
* @throws Exception
6088
* @noinspection PhpUnused
@@ -141,4 +169,9 @@ public function toString(): string
141169
{
142170
return json_encode($this->toArray());
143171
}
172+
173+
public function getHttpStatusCode(): int
174+
{
175+
return 200;
176+
}
144177
}

src/Entity/Card.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Readdle\StripeHttpClientMock\Entity;
5+
6+
class Card extends AbstractEntity
7+
{
8+
protected array $props = [
9+
'address_city' => null,
10+
'address_country' => null,
11+
'address_line1' => null,
12+
'address_line1_check' => null,
13+
'address_line2' => null,
14+
'address_state' => null,
15+
'address_zip' => null,
16+
'address_zip_check' => null,
17+
'brand' => null,
18+
'country' => null,
19+
'customer' => null,
20+
'cvc_check' => null,
21+
'dynamic_last4' => null,
22+
'exp_month' => null,
23+
'exp_year' => null,
24+
'fingerprint' => null,
25+
'funding' => null,
26+
'last4' => null,
27+
'metadata' => [],
28+
'name' => null,
29+
'tokenization_method' => null,
30+
];
31+
}

src/Entity/Charge.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Readdle\StripeHttpClientMock\Entity;
5+
6+
class Charge extends AbstractEntity
7+
{
8+
protected array $props = [
9+
'amount' => null,
10+
'amount_captured' => null,
11+
'amount_refunded' => null,
12+
'application' => null,
13+
'application_fee' => null,
14+
'application_fee_amount' => null,
15+
'balance_transaction' => null,
16+
'billing_details' => [],
17+
'calculated_statement_descriptor' => null,
18+
'captured' => false,
19+
'created' => null,
20+
'currency' => null,
21+
'customer' => null,
22+
'description' => null,
23+
'disputed' => false,
24+
'failure_balance_transaction' => null,
25+
'failure_code' => null,
26+
'failure_message' => null,
27+
'fraud_details' => [],
28+
'invoice' => null,
29+
'livemode' => false,
30+
'metadata' => [],
31+
'on_behalf_of' => null,
32+
'outcome' => [],
33+
'paid' => false,
34+
'payment_intent' => null,
35+
'payment_method' => null,
36+
'payment_method_details' => [],
37+
'receipt_email' => null,
38+
'receipt_number' => null,
39+
'receipt_url' => null,
40+
'refunded' => false,
41+
'refunds' => [],
42+
'review' => null,
43+
'shipping' => null,
44+
'source_transfer' => null,
45+
'statement_descriptor' => null,
46+
'statement_descriptor_suffix' => null,
47+
'status' => null,
48+
'transfer_data' => null,
49+
'transfer_group' => null,
50+
];
51+
52+
public static function prefix(): string
53+
{
54+
return 'ch';
55+
}
56+
}

src/Entity/Customer.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
namespace Readdle\StripeHttpClientMock\Entity;
55

6+
use Readdle\StripeHttpClientMock\Collection;
7+
use Readdle\StripeHttpClientMock\EntityManager;
8+
use Readdle\StripeHttpClientMock\ResponseInterface;
9+
610
class Customer extends AbstractEntity
711
{
812
protected array $props = [
@@ -39,4 +43,15 @@ public static function prefix(): string
3943
{
4044
return 'cus';
4145
}
46+
47+
public function subAction(string $action, array $params): ResponseInterface
48+
{
49+
if ($action === 'sources') {
50+
$sources = new Collection();
51+
$sources->addCollection(EntityManager::listEntity('card', ['customer' => $this->props['id']]));
52+
return $sources;
53+
}
54+
55+
return parent::subAction($action, $params);
56+
}
4257
}

src/Entity/Invoice.php

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ class Invoice extends AbstractEntity
9595
'webhooks_delivered_at' => null,
9696
];
9797

98-
protected static array $expandableProps = ['payment_intent'];
98+
protected static array $expandableProps = [
99+
'customer',
100+
'payment_intent',
101+
'subscription',
102+
];
99103

100104
protected static array $subActions = [
101105
'finalize' => 'finalize',
@@ -121,22 +125,49 @@ public static function create(string $id, array $props = []): ResponseInterface
121125
$props['payment_intent'] = $paymentIntent->id;
122126
}
123127

124-
$lines = new Collection();
128+
if (array_key_exists('default_tax_rates', $props)) {
129+
$props['default_tax_rates'] = array_map(
130+
fn ($defaultTaxRate) =>
131+
is_array($defaultTaxRate)
132+
? $defaultTaxRate
133+
: EntityManager::retrieveEntity('tax_rate', $defaultTaxRate)->toArray(),
134+
$props['default_tax_rates']
135+
);
136+
}
125137

126-
/** @noinspection SpellCheckingInspection */
127-
$pendingInvoiceItems = EntityManager::listEntity('invoiceitem', ['customer' => $props['customer']])->data;
138+
if (!array_key_exists('lines', $props)) {
139+
$lines = new Collection();
128140

129-
if (!empty($pendingInvoiceItems)) {
130-
foreach ($pendingInvoiceItems as $pendingInvoiceItem) {
131-
$lines->add(LineItem::createFromInvoiceItem($pendingInvoiceItem));
141+
/** @noinspection SpellCheckingInspection */
142+
$pendingInvoiceItems = EntityManager::listEntity('invoiceitem', ['customer' => $props['customer']])->data;
143+
144+
if (!empty($pendingInvoiceItems)) {
145+
foreach ($pendingInvoiceItems as $pendingInvoiceItem) {
146+
$lines->add(LineItem::createFromInvoiceItem($pendingInvoiceItem));
147+
}
132148
}
133-
}
134149

135-
$props['lines'] = $lines->toArray();
150+
$props['lines'] = $lines->toArray();
151+
}
136152

137153
return parent::create($id, $props);
138154
}
139155

156+
public function update(array $props): ResponseInterface
157+
{
158+
if (array_key_exists('default_tax_rates', $props)) {
159+
$props['default_tax_rates'] = array_map(
160+
fn ($defaultTaxRate) =>
161+
is_array($defaultTaxRate)
162+
? $defaultTaxRate
163+
: EntityManager::retrieveEntity('tax_rate', $defaultTaxRate)->toArray(),
164+
$props['default_tax_rates']
165+
);
166+
}
167+
168+
return parent::update($props);
169+
}
170+
140171
public static function parseUrlTail(string $tail): array
141172
{
142173
$parsedTail = parent::parseUrlTail($tail);

src/Entity/PaymentIntent.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ class PaymentIntent extends AbstractEntity
5353
'transfer_group' => null,
5454
];
5555

56+
protected static array $expandableProps = [
57+
'payment_method',
58+
'invoice',
59+
];
60+
5661
protected static array $subActions = [
5762
'cancel' => 'cancel',
5863
];

0 commit comments

Comments
 (0)