From 380bc549f7a02f2e5cdda37af201d4c3f320eb19 Mon Sep 17 00:00:00 2001 From: Marcial Paul Gargoles Date: Tue, 7 Sep 2021 19:31:12 +0800 Subject: [PATCH] =?UTF-8?q?Initial=20release=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 24 +++++- composer.json | 24 ++++++ examples/CancelingCharge.php | 27 +++++++ examples/CreatingCharge.php | 55 +++++++++++++ examples/CreatingCheckout.php | 44 +++++++++++ examples/CreatingInvoice.php | 45 +++++++++++ examples/DeletingCheckout.php | 26 +++++++ examples/GettingChargeInfo.php | 25 ++++++ examples/GettingCheckoutInfo.php | 26 +++++++ examples/GettingEventInfo.php | 25 ++++++ examples/GettingInvoiceInfo.php | 25 ++++++ examples/ListingCharges.php | 23 ++++++ examples/ListingCheckouts.php | 23 ++++++ examples/ListingEvents.php | 23 ++++++ examples/ListingInvoices.php | 23 ++++++ examples/ResolveInvoice.php | 25 ++++++ examples/ResolvingCharge.php | 25 ++++++ examples/UpdatingCheckout.php | 38 +++++++++ examples/VoidInvoice.php | 25 ++++++ examples/Webhook.php | 26 +++++++ src/ApiRequest.php | 28 +++++++ src/Charge.php | 76 ++++++++++++++++++ src/Checkout.php | 77 +++++++++++++++++++ src/Coinbase.php | 36 +++++++++ src/Exceptions/ApiException.php | 10 +++ src/Exceptions/InputException.php | 10 +++ src/Exceptions/InvalidRequestException.php | 10 +++ src/Exceptions/RateLimitExceededException.php | 10 +++ src/Exceptions/WebhookPayloadException.php | 10 +++ src/Exceptions/WebhookSignatureException.php | 10 +++ src/Interface/ApiRequest.php | 13 ++++ src/Interface/Charge.php | 13 ++++ src/Interface/Checkout.php | 15 ++++ src/Interface/Coinbase.php | 20 +++++ src/Interface/Event.php | 9 +++ src/Interface/Invoice.php | 9 +++ src/Interface/Webhook.php | 21 +++++ src/Invoice.php | 49 ++++++++++++ src/UpdateCheckout.php | 39 ++++++++++ src/Utils/ApiRequest.php | 50 ++++++++++++ src/Utils/Charge.php | 44 +++++++++++ src/Utils/Checkout.php | 43 +++++++++++ src/Utils/Event.php | 24 ++++++ src/Utils/Invoice.php | 44 +++++++++++ src/Webhook.php | 33 ++++++++ 46 files changed, 1280 insertions(+), 1 deletion(-) create mode 100644 composer.json create mode 100644 examples/CancelingCharge.php create mode 100644 examples/CreatingCharge.php create mode 100644 examples/CreatingCheckout.php create mode 100644 examples/CreatingInvoice.php create mode 100644 examples/DeletingCheckout.php create mode 100644 examples/GettingChargeInfo.php create mode 100644 examples/GettingCheckoutInfo.php create mode 100644 examples/GettingEventInfo.php create mode 100644 examples/GettingInvoiceInfo.php create mode 100644 examples/ListingCharges.php create mode 100644 examples/ListingCheckouts.php create mode 100644 examples/ListingEvents.php create mode 100644 examples/ListingInvoices.php create mode 100644 examples/ResolveInvoice.php create mode 100644 examples/ResolvingCharge.php create mode 100644 examples/UpdatingCheckout.php create mode 100644 examples/VoidInvoice.php create mode 100644 examples/Webhook.php create mode 100644 src/ApiRequest.php create mode 100644 src/Charge.php create mode 100644 src/Checkout.php create mode 100644 src/Coinbase.php create mode 100644 src/Exceptions/ApiException.php create mode 100644 src/Exceptions/InputException.php create mode 100644 src/Exceptions/InvalidRequestException.php create mode 100644 src/Exceptions/RateLimitExceededException.php create mode 100644 src/Exceptions/WebhookPayloadException.php create mode 100644 src/Exceptions/WebhookSignatureException.php create mode 100644 src/Interface/ApiRequest.php create mode 100644 src/Interface/Charge.php create mode 100644 src/Interface/Checkout.php create mode 100644 src/Interface/Coinbase.php create mode 100644 src/Interface/Event.php create mode 100644 src/Interface/Invoice.php create mode 100644 src/Interface/Webhook.php create mode 100644 src/Invoice.php create mode 100644 src/UpdateCheckout.php create mode 100644 src/Utils/ApiRequest.php create mode 100644 src/Utils/Charge.php create mode 100644 src/Utils/Checkout.php create mode 100644 src/Utils/Event.php create mode 100644 src/Utils/Invoice.php create mode 100644 src/Webhook.php diff --git a/.gitignore b/.gitignore index a67d42b..62f1787 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ composer.phar /vendor/ +composer.lock # Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control # You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file diff --git a/README.md b/README.md index 4e21b5c..7f77d32 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,24 @@ # Coinbase commerce PHP8 - Coinbase commerce library for PHP8. works in Laravel8 + +Coinbase commerce library for PHP8. works in Laravel8 + +Library is based in https://commerce.coinbase.com/docs/api/ + +\*\*NOTE: help me improve this repo + +# Installation + +marcialpaulg/coinbase-commerce-php8 is available in [Packagist](https://packagist.org/), and installation via [Composer](https://getcomposer.org/) + +run +`composer require marcialpaulg/coinbase-commerce-php8` + +Note that the vendor folder and the `vendor/autoload.php` script are generated by `Composer`; they are not part of this library. + +# Usage + +check our [examples](https://github.com/marcialpaulg/coinbase-commerce-php8/tree/main/examples) + +# License + +marcialpaulg/coinbase-commerce-php8 is open-sourced software licensed under the [MIT license](LICENSE). diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..31ca058 --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "marcialpaulg/coinbase-commerce-php8", + "description": "Coinbase commerce PHP8 library", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "Marcial Paul Gargoles", + "email": "im.codename@gmail.com" + } + ], + "require": { + "php": "^8.0", + "guzzlehttp/guzzle": "^7.0.1", + "illuminate/collections": "^8.0" + }, + "autoload": { + "psr-4": { + "MarcialPaulG\\Coinbase\\": "src/" + } + }, + "prefer-stable": true, + "minimum-stability": "dev" +} diff --git a/examples/CancelingCharge.php b/examples/CancelingCharge.php new file mode 100644 index 0000000..fd88740 --- /dev/null +++ b/examples/CancelingCharge.php @@ -0,0 +1,27 @@ +cancelCharge($charge_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/CreatingCharge.php b/examples/CreatingCharge.php new file mode 100644 index 0000000..17844e0 --- /dev/null +++ b/examples/CreatingCharge.php @@ -0,0 +1,55 @@ + "id_1005", + "customer_name" => "Satoshi Nakamoto" + ], + redirect_url: "https://charge/completed/page", + cancel_url: "https://charge/canceled/page" + ); + + $data = $coinbase->request($charge); + + // OR + + $data = $coinbase->createCharge( + name: "The Sovereign Individual", + description: "Mastering the Transition to the Information Age", + pricing_type: "fixed_price", + amount: "100.00", + currency: "USD", + metadata: [ + "customer_id" => "id_1005", + "customer_name" => "Satoshi Nakamoto" + ], + redirect_url: "https://charge/completed/page", + cancel_url: "https://charge/canceled/page" + ); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/CreatingCheckout.php b/examples/CreatingCheckout.php new file mode 100644 index 0000000..eec6bc7 --- /dev/null +++ b/examples/CreatingCheckout.php @@ -0,0 +1,44 @@ +request($checkout); + + // OR + + $data = $coinbase->createCheckout( + name: "The Sovereign Individual", + description: "Mastering the Transition to the Information Age", + pricing_type: "fixed_price", + amount: "100.00", + currency: "USD", + requested_info: ['email', 'name'], + ); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/CreatingInvoice.php b/examples/CreatingInvoice.php new file mode 100644 index 0000000..6f7af10 --- /dev/null +++ b/examples/CreatingInvoice.php @@ -0,0 +1,45 @@ +request($invoice); + + // OR + + $data = $coinbase->createInvoice( + business_name: "The Sovereign Individual", + customer_email: "qweqwe@gmaiol.com", + amount: "100.00", + currency: "USD", + customer_name: "Mars", + memo: "for test" + ); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/DeletingCheckout.php b/examples/DeletingCheckout.php new file mode 100644 index 0000000..cda17a8 --- /dev/null +++ b/examples/DeletingCheckout.php @@ -0,0 +1,26 @@ +deleteCheckout($checkout_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/GettingChargeInfo.php b/examples/GettingChargeInfo.php new file mode 100644 index 0000000..6ba3e68 --- /dev/null +++ b/examples/GettingChargeInfo.php @@ -0,0 +1,25 @@ +getCharge($charge_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/GettingCheckoutInfo.php b/examples/GettingCheckoutInfo.php new file mode 100644 index 0000000..288c02a --- /dev/null +++ b/examples/GettingCheckoutInfo.php @@ -0,0 +1,26 @@ +getCheckout($checkout_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/GettingEventInfo.php b/examples/GettingEventInfo.php new file mode 100644 index 0000000..c211604 --- /dev/null +++ b/examples/GettingEventInfo.php @@ -0,0 +1,25 @@ +getEvent($event_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/GettingInvoiceInfo.php b/examples/GettingInvoiceInfo.php new file mode 100644 index 0000000..fb8835f --- /dev/null +++ b/examples/GettingInvoiceInfo.php @@ -0,0 +1,25 @@ +getInvoice($invoice_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ListingCharges.php b/examples/ListingCharges.php new file mode 100644 index 0000000..b2be6a0 --- /dev/null +++ b/examples/ListingCharges.php @@ -0,0 +1,23 @@ +listCharges(); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ListingCheckouts.php b/examples/ListingCheckouts.php new file mode 100644 index 0000000..bc304b2 --- /dev/null +++ b/examples/ListingCheckouts.php @@ -0,0 +1,23 @@ +listCheckouts(); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ListingEvents.php b/examples/ListingEvents.php new file mode 100644 index 0000000..1b04b35 --- /dev/null +++ b/examples/ListingEvents.php @@ -0,0 +1,23 @@ +listEvents(); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ListingInvoices.php b/examples/ListingInvoices.php new file mode 100644 index 0000000..34a4884 --- /dev/null +++ b/examples/ListingInvoices.php @@ -0,0 +1,23 @@ +listInvoices(); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ResolveInvoice.php b/examples/ResolveInvoice.php new file mode 100644 index 0000000..4130bb8 --- /dev/null +++ b/examples/ResolveInvoice.php @@ -0,0 +1,25 @@ +resolveInvoice($invoice_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/ResolvingCharge.php b/examples/ResolvingCharge.php new file mode 100644 index 0000000..a1e3e17 --- /dev/null +++ b/examples/ResolvingCharge.php @@ -0,0 +1,25 @@ +resolveCharge($charge_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/UpdatingCheckout.php b/examples/UpdatingCheckout.php new file mode 100644 index 0000000..3ed2308 --- /dev/null +++ b/examples/UpdatingCheckout.php @@ -0,0 +1,38 @@ + '2000', + 'currency' => 'PHP' + ], + requested_info: ['name'] + ); + + $data = $coinbase->updateCheckout($update_checkout); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/VoidInvoice.php b/examples/VoidInvoice.php new file mode 100644 index 0000000..2ece270 --- /dev/null +++ b/examples/VoidInvoice.php @@ -0,0 +1,25 @@ +voidInvoice($invoice_code_id); +} catch (InvalidRequestException | RateLimitExceededException $e) { + + echo $e->getMessage(); +} diff --git a/examples/Webhook.php b/examples/Webhook.php new file mode 100644 index 0000000..22e05c3 --- /dev/null +++ b/examples/Webhook.php @@ -0,0 +1,26 @@ +validate(); +} catch (WebhookPayloadException | WebhookSignatureException $e) { + + die($e->getMessage()); +} + +// it's safe now to process the payload +// process event \ No newline at end of file diff --git a/src/ApiRequest.php b/src/ApiRequest.php new file mode 100644 index 0000000..6420952 --- /dev/null +++ b/src/ApiRequest.php @@ -0,0 +1,28 @@ +method; + } + + public function getUri(): string + { + return $this->uri; + } + + public function getData(): array + { + return $this->params; + } +} diff --git a/src/Charge.php b/src/Charge.php new file mode 100644 index 0000000..d7ee39f --- /dev/null +++ b/src/Charge.php @@ -0,0 +1,76 @@ +pricing_type, [ + static::NO_PRICE, + static::FIXED_PRICE + ])) { + + throw new InputException('Charge request error: [' . static::NO_PRICE . ', ' . static::FIXED_PRICE . '] only'); + } + + if (empty($this->amount)) { + + $this->pricing_type = static::NO_PRICE; + } else { + + $this->pricing_type = static::FIXED_PRICE; + + $this->local_price = [ + 'amount' => $this->amount, + 'currency' => $this->currency + ]; + } + } + + public function getMethod(): string + { + return 'POST'; + } + + public function getUri(): string + { + return static::RESOURCE_URI; + } + + public function getData(): array + { + return [ + 'name' => $this->name, + 'description' => $this->description, + 'pricing_type' => $this->pricing_type, + 'local_price' => $this->local_price, + 'metadata' => $this->metadata, + 'redirect_url' => $this->redirect_url, + 'cancel_url' => $this->cancel_url, + ]; + } +} diff --git a/src/Checkout.php b/src/Checkout.php new file mode 100644 index 0000000..f180ee4 --- /dev/null +++ b/src/Checkout.php @@ -0,0 +1,77 @@ +pricing_type, [ + static::NO_PRICE, + static::FIXED_PRICE + ])) { + + throw new InputException('Checkout request error: [' . static::NO_PRICE . ', ' . static::FIXED_PRICE . '] only'); + } + + if (empty($this->amount)) { + + $this->pricing_type = static::NO_PRICE; + } else { + + $this->pricing_type = static::FIXED_PRICE; + + $this->local_price = [ + 'amount' => $this->amount, + 'currency' => $this->currency + ]; + } + + $this->requested_info = collect($this->requested_info)->filter(function ($info) { + + return in_array($info, static::REQUEST_INFO); + })->toArray(); + } + + public function getMethod(): string + { + return 'POST'; + } + + public function getUri(): string + { + return static::RESOURCE_URI; + } + + public function getData(): array + { + return [ + 'name' => $this->name, + 'description' => $this->description, + 'pricing_type' => $this->pricing_type, + 'local_price' => $this->local_price, + 'requested_info' => $this->requested_info + ]; + } +} diff --git a/src/Coinbase.php b/src/Coinbase.php new file mode 100644 index 0000000..d189527 --- /dev/null +++ b/src/Coinbase.php @@ -0,0 +1,36 @@ +http_client_handler = new Client([ + 'base_uri' => static::BASE_API_URL, + 'headers' => [ + static::API_KEY_HEADER_NAME => $api_key, + static::API_VERSION_HEADER_NAME => static::API_LATEST_VERSION + ], + 'http_errors' => false + ]); + } +} diff --git a/src/Exceptions/ApiException.php b/src/Exceptions/ApiException.php new file mode 100644 index 0000000..5af6326 --- /dev/null +++ b/src/Exceptions/ApiException.php @@ -0,0 +1,10 @@ + '0.00', + 'currency' => 'USD' + ]; + + public function __construct( + public string $business_name, + public string $customer_email, + public string $amount, + public string $currency, + public string|null $customer_name, + public string|null $memo, + ) { + + $this->local_price['amount'] = $this->amount; + + $this->local_price['currency'] = $this->currency; + } + + public function getMethod(): string + { + return 'POST'; + } + + public function getUri(): string + { + return static::RESOURCE_URI; + } + + public function getData(): array + { + return [ + 'business_name' => $this->business_name, + 'customer_email' => $this->customer_email, + 'customer_name' => $this->customer_name, + 'local_price' => $this->local_price, + 'memo' => $this->memo, + ]; + } +} diff --git a/src/UpdateCheckout.php b/src/UpdateCheckout.php new file mode 100644 index 0000000..066d3e4 --- /dev/null +++ b/src/UpdateCheckout.php @@ -0,0 +1,39 @@ +id}"; + } + + public function getData(): array + { + return [ + 'name' => $this->name, + 'description' => $this->description, + 'local_price' => $this->local_price, + 'requested_info' => $this->requested_info + ]; + } +} diff --git a/src/Utils/ApiRequest.php b/src/Utils/ApiRequest.php new file mode 100644 index 0000000..19f0387 --- /dev/null +++ b/src/Utils/ApiRequest.php @@ -0,0 +1,50 @@ +getUri(), '/'); + + $options = []; + + $params = $apiRequest->getData(); + + if (!empty($params)) { + + $options['json'] = $params; + } + + $response = $this->http_client_handler->request( + $apiRequest->getMethod(), + $uri, + $options + ); + + $contents = $response->getBody()->getContents(); + + if ($this->throw_exceptions) { + + match ($response->getStatusCode()) { + + 400 => throw new InvalidRequestException($contents), + + 429 => throw new RateLimitExceededException($contents), + + default => null + }; + } + + + + return json_decode($contents, true); + } +} diff --git a/src/Utils/Charge.php b/src/Utils/Charge.php new file mode 100644 index 0000000..0f21002 --- /dev/null +++ b/src/Utils/Charge.php @@ -0,0 +1,44 @@ +request($request); + } + + public function getCharge(string $charge_code_id): array + { + $request = new ApiRequest('GET', CoinbaseCharge::RESOURCE_URI . "/{$charge_code_id}"); + + return $this->request($request); + } + + public function createCharge(...$args): array + { + $request = new CoinbaseCharge(...$args); + + return $this->request($request); + } + + public function cancelCharge(string $charge_code_id): array + { + $request = new ApiRequest('POST', CoinbaseCharge::RESOURCE_URI . "/{$charge_code_id}/cancel"); + + return $this->request($request); + } + + public function resolveCharge(string $charge_code_id): array + { + $request = new ApiRequest('POST', CoinbaseCharge::RESOURCE_URI . "/{$charge_code_id}/resolve"); + + return $this->request($request); + } +} diff --git a/src/Utils/Checkout.php b/src/Utils/Checkout.php new file mode 100644 index 0000000..36e3c2f --- /dev/null +++ b/src/Utils/Checkout.php @@ -0,0 +1,43 @@ +request($request); + } + + public function getCheckout(string $checkout_id): array + { + $request = new ApiRequest('GET', CoinbaseCheckout::RESOURCE_URI . "/{$checkout_id}"); + + return $this->request($request); + } + + public function createCheckout(...$args): array + { + $request = new CoinbaseCheckout(...$args); + + return $this->request($request); + } + + public function updateCheckout(UpdateCheckout $update_checkout): array + { + return $this->request($update_checkout); + } + + public function deleteCheckout(string $checkout_id): array + { + $request = new ApiRequest('DELETE', CoinbaseCheckout::RESOURCE_URI . "/{$checkout_id}"); + + return $this->request($request); + } +} diff --git a/src/Utils/Event.php b/src/Utils/Event.php new file mode 100644 index 0000000..9637185 --- /dev/null +++ b/src/Utils/Event.php @@ -0,0 +1,24 @@ +request($request); + } + + public function getEvent(string $event_id) + { + $request = new ApiRequest('GET', InterfaceEvent::RESOURCE_URI . "/{$event_id}"); + + return $this->request($request); + } +} diff --git a/src/Utils/Invoice.php b/src/Utils/Invoice.php new file mode 100644 index 0000000..aef0a13 --- /dev/null +++ b/src/Utils/Invoice.php @@ -0,0 +1,44 @@ +request($request); + } + + public function getInvoice(string $invoice_code_id): array + { + $request = new ApiRequest('GET', CoinbaseInvoice::RESOURCE_URI . "/{$invoice_code_id}"); + + return $this->request($request); + } + + public function createInvoice(...$args): array + { + $request = new CoinbaseInvoice(...$args); + + return $this->request($request); + } + + public function voidInvoice(string $invoice_code_id): array + { + $request = new ApiRequest('PUT', CoinbaseInvoice::RESOURCE_URI . "/{$invoice_code_id}/void"); + + return $this->request($request); + } + + public function resolveInvoice(string $invoice_code_id): array + { + $request = new ApiRequest('POST', CoinbaseInvoice::RESOURCE_URI . "/{$invoice_code_id}/resolve"); + + return $this->request($request); + } +} diff --git a/src/Webhook.php b/src/Webhook.php new file mode 100644 index 0000000..9842230 --- /dev/null +++ b/src/Webhook.php @@ -0,0 +1,33 @@ +payload, true); + + if (json_last_error() || !isset($data['event'])) { + + throw new WebhookPayloadException('Invalid payload provided.'); + } + + $computedSignature = hash_hmac('sha256', $this->payload, $this->secret_key); + + if (!hash_equals($this->signature, $computedSignature)) { + + throw new WebhookSignatureException('Signature mismatched!'); + } + } +}