Skip to content

Commit 4f73a85

Browse files
authored
Merge pull request #382 from SUMO-Kwiatkowski/subscriptions
Subscriptions
2 parents bf05041 + d64fc20 commit 4f73a85

File tree

14 files changed

+962
-5
lines changed

14 files changed

+962
-5
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ composer.phar
1313
test/unit/_html
1414

1515
PrivateKey.key
16+
17+
# Local development
18+
.lando.yml
19+
1620
BitPay.config.json
1721
**/BitPay.config.json
1822
BitPay.config.yml
19-
**/BitPay.config.yml
23+
**/BitPay.config.yml

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"guzzlehttp/guzzle": "^7.9",
2020
"symfony/yaml": "^5.4 || ^6.4 || ^7.0",
2121
"netresearch/jsonmapper": "^5.0",
22-
"symfony/console": "^4.4 || ^5.4 || ^6.4"
22+
"symfony/console": "^4.4 || ^5.4 || ^6.4 || ^7.3.1"
2323
},
2424
"authors": [
2525
{
@@ -48,4 +48,4 @@
4848
"BitPaySDK\\Functional\\": "test/functional/BitPaySDK"
4949
}
5050
}
51-
}
51+
}

composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/BitPaySDK/Client.php

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use BitPaySDK\Client\RateClient;
1919
use BitPaySDK\Client\RefundClient;
2020
use BitPaySDK\Client\SettlementClient;
21+
use BitPaySDK\Client\SubscriptionClient;
2122
use BitPaySDK\Client\TokenClient;
2223
use BitPaySDK\Client\WalletClient;
2324
use BitPaySDK\Exceptions\BitPayApiException;
@@ -36,6 +37,7 @@
3637
use BitPaySDK\Model\Rate\Rate;
3738
use BitPaySDK\Model\Rate\Rates;
3839
use BitPaySDK\Model\Settlement\Settlement;
40+
use BitPaySDK\Model\Subscription\Subscription;
3941
use BitPaySDK\Model\Wallet\Wallet;
4042
use BitPaySDK\Util\RESTcli\RESTcli;
4143
use Exception;
@@ -576,7 +578,7 @@ public function getBill(string $billId, string $facade = Facade::MERCHANT, bool
576578
*
577579
* @see https://developer.bitpay.com/reference/retrieve-bills-by-status Retrieve Bills by Status
578580
*
579-
* @param string|null The status to filter the bills.
581+
* @param string|null $status The status to filter the bills.
580582
* @return Bill[]
581583
* @throws BitPayApiException
582584
* @throws BitPayGenericException
@@ -625,6 +627,75 @@ public function deliverBill(string $billId, string $billToken, bool $signRequest
625627
return $billClient->deliver($billId, $billToken, $signRequest);
626628
}
627629

630+
/**
631+
* Create a BitPay Subscription.
632+
*
633+
* @see https://developer.bitpay.com/reference/create-a-subscription Create a Subscription
634+
*
635+
* @param Subscription $subscription A Subscription object with request parameters defined.
636+
* @return Subscription Created Subscription object
637+
* @throws BitPayApiException
638+
* @throws BitPayGenericException
639+
*/
640+
public function createSubscription(Subscription $subscription): Subscription
641+
{
642+
$subscriptionClient = $this->getSubscriptionClient();
643+
644+
return $subscriptionClient->create($subscription);
645+
}
646+
647+
/**
648+
* Retrieve a BitPay subscription by its ID.
649+
*
650+
* @see https://developer.bitpay.com/reference/retrieve-a-subscription Retrieve a Subscription
651+
*
652+
* @param string $subscriptionId The ID of the subscription to retrieve.
653+
* @return Subscription
654+
* @throws BitPayApiException
655+
* @throws BitPayGenericException
656+
*/
657+
public function getSubscription(string $subscriptionId): Subscription
658+
{
659+
$subscriptionClient = $this->getSubscriptionClient();
660+
661+
return $subscriptionClient->get($subscriptionId);
662+
}
663+
664+
/**
665+
* Retrieve a collection of BitPay subscriptions.
666+
*
667+
* @see https://developer.bitpay.com/reference/retrieve-subscriptions-by-status Retrieve Subscriptions by Status
668+
*
669+
* @param string|null $status The status on which to filter the subscriptions.
670+
* @return Subscription[] Filtered list of Subscription objects
671+
* @throws BitPayApiException
672+
* @throws BitPayGenericException
673+
*/
674+
public function getSubscriptions(?string $status = null): array
675+
{
676+
$subscriptionClient = $this->getSubscriptionClient();
677+
678+
return $subscriptionClient->getSubscriptions($status);
679+
}
680+
681+
/**
682+
* Update a BitPay Subscription.
683+
*
684+
* @see https://developer.bitpay.com/reference/update-a-subscription Update a Subscription
685+
*
686+
* @param Subscription $subscription A Subscription object with the parameters to update defined.
687+
* @param string $subscriptionId The ID of the Subscription to update.
688+
* @return Subscription Updated Subscription object
689+
* @throws BitPayApiException
690+
* @throws BitPayGenericException
691+
*/
692+
public function updateSubscription(Subscription $subscription, string $subscriptionId): Subscription
693+
{
694+
$subscriptionClient = $this->getSubscriptionClient();
695+
696+
return $subscriptionClient->update($subscription, $subscriptionId);
697+
}
698+
628699
/**
629700
* Retrieve the exchange rate table maintained by BitPay.
630701
* @see https://bitpay.com/bitcoin-exchange-rates
@@ -1113,6 +1184,16 @@ protected function getBillClient(): BillClient
11131184
return BillClient::getInstance($this->tokenCache, $this->restCli);
11141185
}
11151186

1187+
/**
1188+
* Gets subscription client
1189+
*
1190+
* @return SubscriptionClient the subscription client
1191+
*/
1192+
protected function getSubscriptionClient(): SubscriptionClient
1193+
{
1194+
return SubscriptionClient::getInstance($this->tokenCache, $this->restCli);
1195+
}
1196+
11161197
/**
11171198
* Gets rate client
11181199
*
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<?php
2+
3+
/**
4+
* Copyright (c) 2025 BitPay
5+
**/
6+
7+
declare(strict_types=1);
8+
9+
namespace BitPaySDK\Client;
10+
11+
use BitPaySDK\Exceptions\BitPayApiException;
12+
use BitPaySDK\Exceptions\BitPayExceptionProvider;
13+
use BitPaySDK\Exceptions\BitPayGenericException;
14+
use BitPaySDK\Model\Subscription\Subscription;
15+
use BitPaySDK\Model\Facade;
16+
use BitPaySDK\Tokens;
17+
use BitPaySDK\Util\JsonMapperFactory;
18+
use BitPaySDK\Util\RESTcli\RESTcli;
19+
use Exception;
20+
21+
/**
22+
* Handles interactions with the subscriptions endpoints.
23+
*
24+
* @package BitPaySDK\Client
25+
* @author BitPay Integrations <[email protected]>
26+
* @license http://www.opensource.org/licenses/mit-license.php MIT
27+
*/
28+
class SubscriptionClient
29+
{
30+
private static ?self $instance = null;
31+
private Tokens $tokenCache;
32+
private RESTcli $restCli;
33+
34+
private function __construct(Tokens $tokenCache, RESTcli $restCli)
35+
{
36+
$this->tokenCache = $tokenCache;
37+
$this->restCli = $restCli;
38+
}
39+
40+
/**
41+
* Factory method for Subscription Client.
42+
*
43+
* @param Tokens $tokenCache
44+
* @param RESTcli $restCli
45+
* @return static
46+
*/
47+
public static function getInstance(Tokens $tokenCache, RESTcli $restCli): self
48+
{
49+
if (!self::$instance) {
50+
self::$instance = new self($tokenCache, $restCli);
51+
}
52+
53+
return self::$instance;
54+
}
55+
56+
/**
57+
* Create a BitPay Subscription.
58+
*
59+
* @param Subscription $subscription A Subscription object with request parameters defined.
60+
* @return Subscription Created Subscription object
61+
* @throws BitPayApiException
62+
* @throws BitPayGenericException
63+
*/
64+
public function create(Subscription $subscription): Subscription
65+
{
66+
$subscription->setToken($this->tokenCache->getTokenByFacade(Facade::MERCHANT));
67+
68+
$responseJson = $this->restCli->post("subscriptions", $subscription->toArray());
69+
70+
try {
71+
return $this->mapJsonToSubscriptionClass($responseJson);
72+
} catch (Exception $e) {
73+
BitPayExceptionProvider::throwDeserializeResourceException('Subscription', $e->getMessage());
74+
}
75+
}
76+
77+
/**
78+
* Retrieve a BitPay subscription by its resource ID.
79+
*
80+
* @param $subscriptionId string The id of the subscription to retrieve.
81+
* @return Subscription Retrieved Subscription object
82+
* @throws BitPayApiException
83+
* @throws BitPayGenericException
84+
*/
85+
public function get(string $subscriptionId): Subscription
86+
{
87+
$params = [];
88+
$params["token"] = $this->tokenCache->getTokenByFacade(Facade::MERCHANT);
89+
90+
$responseJson = $this->restCli->get("subscriptions/" . $subscriptionId, $params);
91+
92+
try {
93+
return $this->mapJsonToSubscriptionClass($responseJson);
94+
} catch (Exception $e) {
95+
BitPayExceptionProvider::throwDeserializeResourceException('Subscription', $e->getMessage());
96+
}
97+
}
98+
99+
/**
100+
* Retrieve a collection of BitPay subscriptions.
101+
*
102+
* @param string|null $status The status to filter the subscriptions.
103+
* @return Subscription[] Filtered list of Subscription objects
104+
* @throws BitPayApiException
105+
* @throws BitPayGenericException
106+
*/
107+
public function getSubscriptions(?string $status = null): array
108+
{
109+
$params = [];
110+
$params["token"] = $this->tokenCache->getTokenByFacade(Facade::MERCHANT);
111+
if ($status) {
112+
$params["status"] = $status;
113+
}
114+
115+
$responseJson = $this->restCli->get("subscriptions", $params);
116+
117+
try {
118+
$mapper = JsonMapperFactory::create();
119+
return $mapper->mapArray(
120+
json_decode($responseJson, true, 512, JSON_THROW_ON_ERROR),
121+
[],
122+
Subscription::class
123+
);
124+
} catch (Exception $e) {
125+
BitPayExceptionProvider::throwDeserializeResourceException('Subscription', $e->getMessage());
126+
}
127+
}
128+
129+
/**
130+
* Update a BitPay Subscription.
131+
*
132+
* @param Subscription $subscription A Subscription object with the parameters to update defined.
133+
* @param string $subscriptionId The ID of the Subscription to update.
134+
* @return Subscription Updated Subscription object
135+
* @throws BitPayApiException
136+
* @throws BitPayGenericException
137+
*/
138+
public function update(Subscription $subscription, string $subscriptionId): Subscription
139+
{
140+
$subscriptionToken = $this->get($subscription->getId())->getToken();
141+
$subscription->setToken($subscriptionToken);
142+
143+
$responseJson = $this->restCli->update("subscriptions/" . $subscriptionId, $subscription->toArray());
144+
145+
try {
146+
$mapper = JsonMapperFactory::create();
147+
148+
return $mapper->map(
149+
json_decode($responseJson, true, 512, JSON_THROW_ON_ERROR),
150+
$subscription
151+
);
152+
} catch (Exception $e) {
153+
BitPayExceptionProvider::throwDeserializeResourceException('Subscription', $e->getMessage());
154+
}
155+
}
156+
157+
/**
158+
* @param string|null $responseJson
159+
* @return Subscription
160+
* @throws \JsonException
161+
* @throws \JsonMapper_Exception
162+
*/
163+
private function mapJsonToSubscriptionClass(?string $responseJson): Subscription
164+
{
165+
$mapper = JsonMapperFactory::create();
166+
167+
return $mapper->map(
168+
json_decode($responseJson, true, 512, JSON_THROW_ON_ERROR),
169+
new Subscription()
170+
);
171+
}
172+
}

src/BitPaySDK/Env.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ interface Env
2020
public const BITPAY_PLUGIN_INFO = "BitPay_PHP_Client_v9.2.2";
2121
public const BITPAY_API_FRAME = "std";
2222
public const BITPAY_API_FRAME_VERSION = "1.0.0";
23+
public const BITPAY_DATETIME_FORMAT = 'Y-m-d\TH:i:s\Z';
2324
}

src/BitPaySDK/Model/Settlement/Settlement.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public function setPayoutInfo(PayoutInfo $payoutInfo): void
141141
* Gets Status of the settlement. Possible statuses are "new", "processing", "rejected" and "completed".
142142
*
143143
* @return string|null
144+
* @see SettlementStatus
144145
*/
145146
public function getStatus(): ?string
146147
{
@@ -151,6 +152,7 @@ public function getStatus(): ?string
151152
* Sets Status of the settlement. Possible statuses are "new", "processing", "rejected" and "completed".
152153
*
153154
* @param string $status
155+
* @see SettlementStatus
154156
*/
155157
public function setStatus(string $status): void
156158
{
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/**
4+
* Copyright (c) 2025 BitPay
5+
**/
6+
7+
/**
8+
* @author BitPay Integrations <[email protected]>
9+
* @license http://www.opensource.org/licenses/mit-license.php MIT
10+
*/
11+
12+
namespace BitPaySDK\Model\Settlement;
13+
14+
/**
15+
* Status of the settlement.
16+
* Possible statuses are "new", "processing", "rejected" and "completed".
17+
*
18+
* @package BitPaySDK\Model\Settlement
19+
* @author BitPay Integrations <[email protected]>
20+
* @license http://www.opensource.org/licenses/mit-license.php MIT
21+
* @see https://developer.bitpay.com/reference/settlements Settlements
22+
*/
23+
interface SettlementStatus
24+
{
25+
public const NEW = "new";
26+
public const PROCESSING = "processing";
27+
public const REJECTED = "rejected";
28+
public const COMPLETED = "completed";
29+
}

0 commit comments

Comments
 (0)