├── config ├── .gitkeep └── laravel-bitpay.php ├── src ├── .DS_Store ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ └── WebhookController.php │ └── Middleware │ │ └── VerifyWebhookSignature.php ├── Constants │ ├── WebhookAutoPopulate.php │ └── BitPayConstants.php ├── LaravelBitpayFacade.php ├── Events │ └── BitpayWebhookReceived.php ├── Actions │ ├── ManageCurrencies.php │ ├── ManageLedgers.php │ ├── ManageExchangeRates.php │ ├── ManageSettlements.php │ ├── ManageRefunds.php │ ├── ManageBills.php │ ├── ManageSubscriptions.php │ ├── ManageInvoices.php │ ├── ManageRecipients.php │ └── ManagePayouts.php ├── Exceptions │ └── InvalidConfigurationException.php ├── LaravelBitpay.php ├── LaravelBitpayServiceProvider.php ├── Traits │ ├── MakesHttpRequests.php │ └── CreateKeypairTrait.php └── Console │ └── CreateKeypairCommand.php ├── .gitignore ├── .github ├── FUNDING.yml ├── tmp_ISSUE_TEMPLATE.yml └── workflows │ └── php.yml ├── tests ├── LaravelBitpayCurrencyTest.php ├── LaravelBitpayRefundTest.php ├── LaravelBitpayRecipientTest.php ├── LaravelBitpayBillTest.php ├── LaravelBitpayInvoiceTest.php ├── LaravelBitpayPayoutTest.php └── LaravelBitpaySubscriptionTest.php ├── phpunit.xml.dist ├── LICENSE.md ├── CONTRIBUTING.md ├── CHANGELOG.md ├── composer.json ├── MIGRATION.md ├── README.md └── .editorconfig /config/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vrajroham/laravel-bitpay/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | composer.lock 3 | docs 4 | vendor 5 | coverage 6 | .idea 7 | .phpunit.result.cache 8 | .php_cs.cache 9 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [vrajroham, alexstewartja] 4 | patreon: vrajroham 5 | custom: ["https://www.buymeacoffee.com/asja", "https://bit.ly/asja_paypal_lara_bitpay"] 6 | -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Currency() instanceof Currency); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/LaravelBitpayRefundTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Refund() instanceof Refund); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/LaravelBitpayRecipientTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::PayoutRecipient() instanceof PayoutRecipient); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Events/BitpayWebhookReceived.php: -------------------------------------------------------------------------------- 1 | payload = $payload; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/LaravelBitpayBillTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Bill() instanceof Bill); 16 | } 17 | 18 | /** @test */ 19 | public function isInstanceOfBillItem() 20 | { 21 | $this->assertEquals(true, LaravelBitpay::BillItem() instanceof BillItem); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Http/Controllers/WebhookController.php: -------------------------------------------------------------------------------- 1 | middleware(VerifyWebhookSignature::class); 14 | } 15 | 16 | public function handleWebhook(Request $request) 17 | { 18 | $payload = json_decode($request->getContent(), true); 19 | BitpayWebhookReceived::dispatch($payload); 20 | 21 | return response('OK', 200); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/LaravelBitpayInvoiceTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Invoice() instanceof BitPaySDKInvoice); 17 | } 18 | 19 | /** @test */ 20 | public function isInstanceOfBuyer() 21 | { 22 | $this->assertEquals(true, LaravelBitpay::Buyer() instanceof Buyer); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Http/Middleware/VerifyWebhookSignature.php: -------------------------------------------------------------------------------- 1 | getMessage(), $exception); 27 | } 28 | 29 | return $next($request); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Actions/ManageCurrencies.php: -------------------------------------------------------------------------------- 1 | setCode($code); 22 | } 23 | 24 | return $currency; 25 | } 26 | 27 | /** 28 | * Fetch the supported currencies. 29 | * 30 | * @link https://bitpay.com/api/#rest-api-resources-currencies-retrieve-the-supported-currencies 31 | * @return array A list of supported BitPay Currency objects. 32 | * @throws BitPayException BitPayException class 33 | */ 34 | public static function getCurrencies(): array 35 | { 36 | return (new self())->client->getCurrencies(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Exceptions/InvalidConfigurationException.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Payout() instanceof Payout); 19 | } 20 | 21 | /** @test */ 22 | public function isInstanceOfPayoutBatch() 23 | { 24 | $this->assertEquals(true, LaravelBitpay::PayoutBatch() instanceof PayoutBatch); 25 | } 26 | 27 | /** @test */ 28 | public function isInstanceOfPayoutInstruction() 29 | { 30 | $this->assertEquals(true, LaravelBitpay::PayoutInstruction( 31 | 10, 32 | RecipientReferenceMethod::EMAIL, 33 | 'test@example.com' 34 | ) instanceof PayoutInstruction); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /tests/LaravelBitpaySubscriptionTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(true, LaravelBitpay::Subscription() instanceof Subscription); 19 | } 20 | 21 | /** @test */ 22 | public function isInstanceOfSubscriptionItem() 23 | { 24 | $this->assertEquals(true, LaravelBitpay::SubscriptionItem() instanceof SubscriptionItem); 25 | } 26 | 27 | /** @test */ 28 | public function isInstanceOfBillData() 29 | { 30 | $this->assertEquals(true, LaravelBitpay::BillData( 31 | Currency::USD, 32 | 'test@example.com', 33 | '2021-12-01T09:00:00Z', 34 | []) instanceof BillData); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Vaibhavraj Roham 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/vrajroham/laravel-bitpay). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /src/LaravelBitpay.php: -------------------------------------------------------------------------------- 1 | setupClient(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/LaravelBitpayServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 19 | $this->publishes([ 20 | __DIR__ . '/../config/laravel-bitpay.php' => config_path('laravel-bitpay.php'), 21 | ], 'config'); 22 | } 23 | 24 | $this->registerRoutes(); 25 | } 26 | 27 | /** 28 | * Register the application services. 29 | */ 30 | public function register() 31 | { 32 | $this->mergeConfigFrom(__DIR__ . '/../config/laravel-bitpay.php', 'laravel-bitpay'); 33 | $this->app->bind('command.laravel-bitpay:createkeypair', CreateKeypairCommand::class); 34 | $this->commands([ 35 | 'command.laravel-bitpay:createkeypair', 36 | ]); 37 | } 38 | 39 | protected function registerRoutes() 40 | { 41 | Route::macro('bitPayWebhook', 42 | function (string $uri = 'laravel-bitpay/webhook') { 43 | Route::post($uri, [WebhookController::class, 'handleWebhook']) 44 | ->name('laravel-bitpay.webhook.capture'); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `vrajroham/laravel-bitpay` will be documented in this file 4 | 5 | --- 6 | 7 | #### v6.0.0 - 2022-12-18 8 | 9 | * Updated BitPay client dependency to v7.1.0 10 | * Bumped minimum PHP version to 7.4 11 | * Implemented the invoice webhook/notification resend action 12 | * Isolated implementation of the `Refunds` resource 13 | 14 | #### v5.3.0 - 2022-06-20 15 | 16 | * Fixed issues, updated deps & corrected typos by @alexstewartja in https://github.com/vrajroham/laravel-bitpay/pull/42 17 | 18 | #### v5.0.1 - 2021-12-18 19 | 20 | * Make newLine() calls compatible with Laravel versions below 8 by @Ririshi in https://github.com/vrajroham/laravel-bitpay/pull/41 21 | 22 | #### v5.0.0 - 2021-12-09 23 | 24 | * v5 Upgrade by @alexstewartja in https://github.com/vrajroham/laravel-bitpay/pull/38: 25 | + Updated BitPay client dependency to v6.0.2111 26 | + Implemented exchange rates (single/pair) 27 | + Deprecated constants that already exist in core library 28 | - Also added return codes that can be used to verify webhooks for the following resources: 29 | + Invoices 30 | + Recipients 31 | + Payouts 32 | + Implemented `Settlements` resource 33 | + Implemented `Ledgers` resource 34 | + Implemented `Recipients` resource 35 | + Implemented `Payouts`/`PayoutBatches` resources 36 | + Added support for `payout` facade 37 | + Updated UX of API Token generation command 38 | + Optimized webhook URL (notificationURL) auto-population 39 | + Added v4 -> v5 to migration guide 40 | 41 | #### v2.0.2 42 | 43 | - Fixed **livenet** token `URL` [#14](https://github.com/vrajroham/laravel-bitpay/pull/14) by [@IvanG11](https://github.com/IvanG11) 44 | -------------------------------------------------------------------------------- /src/Actions/ManageLedgers.php: -------------------------------------------------------------------------------- 1 | client->getLedgers(); 29 | } 30 | 31 | /** 32 | * Retrieve ledger entries. 33 | * 34 | * @link https://bitpay.com/api/#rest-api-resources-ledgers-retrieve-ledger-entries 35 | * 36 | * @param $currency string ISO 4217 3-character currency code for the ledger to retrieve. 37 | * @param $startDate string The start date for fetching ledger entries. Format YYYY-MM-DD 38 | * @param $endDate string The end date for fetching ledger entries. Format YYYY-MM-DD 39 | * 40 | * @return LedgerEntry[] A list of LedgerEntry objects that match the provided filters. 41 | * @throws BitPayException BitPayException class 42 | */ 43 | public static function getLedger(string $currency, string $startDate, string $endDate): array 44 | { 45 | return (new self())->client->getLedger($currency, $startDate, $endDate); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vrajroham/laravel-bitpay", 3 | "description": "BitPay wrapper for laravel", 4 | "keywords": [ 5 | "bitpay", 6 | "BTC", 7 | "laravel" 8 | ], 9 | "homepage": "https://github.com/vrajroham/laravel-bitpay", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Vaibhavraj Roham", 14 | "email": "vaibhavraj@vrajroham.me", 15 | "homepage": "https://vrajroham.me", 16 | "role": "Developer" 17 | }, 18 | { 19 | "name": "Alex Stewart", 20 | "email": "iamalexstewart@gmail.com", 21 | "homepage": "https://github.com/alexstewartja", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": "^7.4 || ^8.0 || ^8.1", 27 | "ext-json": "*", 28 | "bitpay/sdk": "~7.1.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": ">=9.5" 32 | }, 33 | "autoload": { 34 | "psr-4": { 35 | "Vrajroham\\LaravelBitpay\\": "src" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Vrajroham\\LaravelBitpay\\Tests\\": "tests" 41 | } 42 | }, 43 | "scripts": { 44 | "test": "vendor/bin/phpunit", 45 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 46 | }, 47 | "config": { 48 | "sort-packages": true 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "providers": [ 53 | "Vrajroham\\LaravelBitpay\\LaravelBitpayServiceProvider" 54 | ], 55 | "aliases": { 56 | "LaravelBitpay": "Vrajroham\\LaravelBitpay\\LaravelBitpayFacade" 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.github/tmp_ISSUE_TEMPLATE.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug 2 | description: File a bug/issue 3 | title: "[BUG] " 4 | labels: [Bug, Needs Triage] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this? 9 | description: Please search to see if an issue already exists for the bug you encountered. 10 | options: 11 | - label: I have searched the existing issues 12 | required: true 13 | - type: textarea 14 | attributes: 15 | label: Current Behavior 16 | description: A concise description of what you're experiencing. 17 | validations: 18 | required: false 19 | - type: textarea 20 | attributes: 21 | label: Expected Behavior 22 | description: A concise description of what you expected to happen. 23 | validations: 24 | required: false 25 | - type: textarea 26 | attributes: 27 | label: Steps To Reproduce 28 | description: Steps to reproduce the behavior. 29 | placeholder: | 30 | 1. In this environment... 31 | 2. With this config... 32 | 3. Run '...' 33 | 4. See error... 34 | validations: 35 | required: false 36 | - type: textarea 37 | attributes: 38 | label: Environment 39 | description: | 40 | examples: 41 | - **OS**: Ubuntu 20.04 42 | - **PHP**: 7.4 43 | - **Laravel**: 8.0 44 | value: | 45 | - OS: 46 | - PHP: 47 | - Laravel: 48 | render: markdown 49 | validations: 50 | required: false 51 | - type: textarea 52 | attributes: 53 | label: Anything else? 54 | description: | 55 | Links? References? Anything that will give us more context about the issue you are encountering! 56 | 57 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 58 | validations: 59 | required: false 60 | -------------------------------------------------------------------------------- /src/Constants/BitPayConstants.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Constants; 4 | 5 | interface BitPayConstants 6 | { 7 | // TODO: Deprecate 'Invoice Exceptions', 'Subscription Schedules' and 'Settlement Statuses' after upstream merge & update (^6.0): https://github.com/bitpay/php-bitpay-client-v2/pull/69 8 | 9 | const DATETIME_FORMAT = 'Y-m-d\TH:i:s\Z'; 10 | 11 | // Invoice Exceptions 12 | const INVOICE_EXCEPTION_PAIDOVER = 'paidOver'; 13 | const INVOICE_EXCEPTION_PAIDPARTIAL = 'paidPartial'; 14 | 15 | // Subscription Schedules 16 | const SUBSCRIPTION_SCHEDULE_WEEKLY = 'weekly'; 17 | const SUBSCRIPTION_SCHEDULE_MONTHLY = 'monthly'; 18 | const SUBSCRIPTION_SCHEDULE_QUARTERLY = 'quarterly'; 19 | const SUBSCRIPTION_SCHEDULE_YEARLY = 'yearly'; 20 | 21 | // Settlement Statuses 22 | const SETTLEMENT_STATUS_NEW = 'new'; 23 | const SETTLEMENT_STATUS_PROCESSING = 'processing'; 24 | const SETTLEMENT_STATUS_REJECTED = 'rejected'; 25 | const SETTLEMENT_STATUS_COMPLETED = 'completed'; 26 | 27 | // Invoice Webhook Codes 28 | const INVOICE_WEBHOOK_CODES = [ 29 | 1003 => 'invoice_paidInFull', 30 | 1004 => 'invoice_expired', 31 | 1005 => 'invoice_confirmed', 32 | 1006 => 'invoice_completed', 33 | 1012 => 'invoice_manuallyNotified', 34 | 1013 => 'invoice_failedToConfirm', 35 | 1016 => 'invoice_refundComplete', 36 | ]; 37 | 38 | // Recipient Webhook Codes 39 | const RECIPIENT_WEBHOOK_CODES = [ 40 | 4001 => 'recipient_invited', 41 | 4002 => 'recipient_unverified', 42 | 4003 => 'recipient_verified', 43 | 4004 => 'recipient_active', 44 | 4005 => 'recipient_paused', 45 | 4006 => 'recipient_removed', 46 | ]; 47 | 48 | // Payout Webhook Codes 49 | const PAYOUT_WEBHOOK_CODES = [ 50 | 2001 => 'payoutRequest_funded', 51 | 2002 => 'payoutRequest_completed', 52 | 2003 => 'payoutRequest_cancelled', 53 | ]; 54 | } 55 | -------------------------------------------------------------------------------- /MIGRATION.md: -------------------------------------------------------------------------------- 1 | # LaravelBitPay Migration Guide 2 | 3 | This guide will serve as a single source of truth, regarding breaking changes and necessary actions one must take when 4 | upgrading to major versions. 5 | 6 | ## From v4 to v5 7 | 8 | ### Configuration 9 | 10 | - Ensure `BITPAY_*` values inside your `.env` follows the updated naming conventions outlined 11 | here: https://github.com/vrajroham/laravel-bitpay#add-configuration-values. Specifically: 12 | + Removed: 13 | - `BITPAY_PUBLIC_KEY_PATH` 14 | + Changed: 15 | - `BITPAY_TOKEN` renamed to `BITPAY_MERCHANT_TOKEN` 16 | + Added: 17 | - `BITPAY_ENABLE_MERCHANT` 18 | - `BITPAY_ENABLE_PAYOUT` 19 | - `BITPAY_PAYOUT_TOKEN` 20 | 21 | 22 | - Ensure the contents of your `laravel-bitpay.php` config file is on par with the updated one found 23 | here: https://github.com/vrajroham/laravel-bitpay/blob/master/config/laravel-bitpay.php 24 | 25 | ### API Tokens 26 | 27 | LaravelBitPay now supports the `payout` facade, along with generating a Payout API Token, which allows you to interact 28 | with [Recipients](https://bitpay.com/api/#rest-api-resources-recipients) 29 | and [Payouts](https://bitpay.com/api/#rest-api-resources-payouts) resources. 30 | 31 | It's as easy as setting `BITPAY_ENABLE_PAYOUT` to `true` inside your `.env` file then 32 | following: https://github.com/vrajroham/laravel-bitpay#generate-key-pair-and-api-tokens. 33 | 34 | ### Webhooks 35 | 36 | Webhooks are now an optional feature. Your event listener should work the same, but to configure your webhook URL 37 | (notificationURL) please refer here: https://github.com/vrajroham/laravel-bitpay#configure-webhooks-optional. 38 | 39 | ### Constants 40 | 41 | + Removed: 42 | - `BitPayConstants::BILL_STATUS_*` in favor of core `BillStatus::*` 43 | - `BitPayConstants::SUBSCRIPTION_STATUS_*` in favor of core `SubscriptionStatus::*` 44 | + Added: 45 | - `BitPayConstants::INVOICE_EXCEPTION_*` 46 | - `BitPayConstants::INVOICE_WEBHOOK_CODES` 47 | - `BitPayConstants::RECIPIENT_WEBHOOK_CODES` 48 | - `BitPayConstants::PAYOUT_WEBHOOK_CODES` 49 | -------------------------------------------------------------------------------- /src/Traits/MakesHttpRequests.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Traits; 4 | 5 | use BitPaySDK\Client as BitpayClient; 6 | use BitPaySDK\Env; 7 | use BitPaySDK\Exceptions\BitPayException; 8 | use BitPaySDK\Tokens; 9 | use Vrajroham\LaravelBitpay\Exceptions\InvalidConfigurationException; 10 | 11 | 12 | trait MakesHttpRequests 13 | { 14 | /** 15 | * Get the BitPay client. 16 | * 17 | * @throws InvalidConfigurationException 18 | * @throws BitPayException 19 | */ 20 | public function setupClient() 21 | { 22 | $this->validateAndLoadConfig(); 23 | 24 | $this->client = BitpayClient::create()->withData( 25 | 'testnet' == $this->config['network'] ? Env::Test : Env::Prod, 26 | $this->config['private_key'], 27 | new Tokens( 28 | $this->config['merchant_token'], //merchant 29 | $this->config['payout_token'] //payout 30 | ), 31 | $this->config['key_storage_password'] //used to decrypt your private key, if encrypted 32 | ); 33 | } 34 | 35 | /** 36 | * Validate and load the config. 37 | * 38 | * @throws InvalidConfigurationException 39 | */ 40 | public function validateAndLoadConfig() 41 | { 42 | $config = config('laravel-bitpay'); 43 | 44 | if ('livenet' !== $config['network'] && 'testnet' !== $config['network']) { 45 | throw InvalidConfigurationException::invalidNetworkName(); 46 | } 47 | 48 | if (! class_exists($config['key_storage'])) { 49 | throw InvalidConfigurationException::invalidStorageClass(); 50 | } 51 | 52 | if ('' === trim($config['key_storage_password'])) { 53 | throw InvalidConfigurationException::invalidOrEmptyPassword(); 54 | } 55 | 56 | if ((!empty($config['merchant_facade_enabled']) && $config['merchant_facade_enabled']) 57 | && empty($config['merchant_token'])) { 58 | throw InvalidConfigurationException::emptyMerchantToken(); 59 | } 60 | 61 | if ((!empty($config['payout_facade_enabled']) && $config['payout_facade_enabled']) 62 | && empty($config['payout_token'])) { 63 | throw InvalidConfigurationException::emptyPayoutToken(); 64 | } 65 | 66 | $this->config = $config; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Actions/ManageExchangeRates.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use \BitPaySDK\Model\Rate\Rate; 7 | use BitPaySDK\Model\Rate\Rates; 8 | 9 | 10 | trait ManageExchangeRates 11 | { 12 | /** 13 | * Retrieve the exchange rate table maintained by BitPay. 14 | * 15 | * @link https://bitpay.com/exchange-rates 16 | * 17 | * @return Rates A Rates object populated with the BitPay exchange rate table. 18 | * @throws BitPayException BitPayException class 19 | */ 20 | public static function getRates(): Rates 21 | { 22 | return (new self())->client->getRates(); 23 | } 24 | 25 | /** 26 | * Retrieve all the rates for a given cryptocurrency 27 | * 28 | * @link https://bitpay.com/api/#rest-api-resources-rates-retrieve-all-the-rates-for-a-given-cryptocurrency 29 | * 30 | * @param string $baseCurrency The cryptocurrency for which you want to fetch the rates. 31 | * Current supported values are BTC, BCH, ETH, XRP, DOGE and LTC 32 | * 33 | * @return Rates A Rates object populated with the currency rates for the requested baseCurrency. 34 | * @throws BitPayException BitPayException class 35 | */ 36 | public static function getCurrencyRates(string $baseCurrency): Rates 37 | { 38 | return (new self())->client->getCurrencyRates($baseCurrency); 39 | } 40 | 41 | /** 42 | * Retrieve the rate for a cryptocurrency / fiat pair 43 | * 44 | * @link https://bitpay.com/api/#rest-api-resources-rates-retrieve-the-rates-for-a-cryptocurrency-fiat-pair 45 | * 46 | * @param string $baseCurrency The cryptocurrency for which you want to fetch the fiat-equivalent rate. 47 | * Current supported values are BTC, BCH, ETH, XRP, DOGE and LTC 48 | * @param string $currency The fiat currency for which you want to fetch the baseCurrency rate 49 | * 50 | * @return Rate A Rate object populated with the currency rate for the requested baseCurrency. 51 | * @throws BitPayException BitPayException class 52 | */ 53 | public static function getCurrencyPairRate(string $baseCurrency, string $currency): Rate 54 | { 55 | return (new self())->client->getCurrencyPairRate($baseCurrency, $currency); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build_php_74: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup PHP with PECL extension 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: '7.4' 21 | 22 | - name: Validate composer.json and composer.lock 23 | run: composer validate --strict 24 | 25 | - name: Cache Composer packages 26 | id: composer-cache 27 | uses: actions/cache@v2 28 | with: 29 | path: vendor 30 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 31 | restore-keys: | 32 | ${{ runner.os }}-php- 33 | 34 | - name: Install dependencies 35 | run: composer install --prefer-dist --no-progress 36 | 37 | - name: Run test suite 38 | run: composer run-script test 39 | 40 | build_php_80: 41 | 42 | runs-on: ubuntu-latest 43 | 44 | steps: 45 | - uses: actions/checkout@v2 46 | 47 | - name: Setup PHP with PECL extension 48 | uses: shivammathur/setup-php@v2 49 | with: 50 | php-version: '8.0' 51 | 52 | - name: Validate composer.json and composer.lock 53 | run: composer validate --strict 54 | 55 | - name: Cache Composer packages 56 | id: composer-cache 57 | uses: actions/cache@v2 58 | with: 59 | path: vendor 60 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 61 | restore-keys: | 62 | ${{ runner.os }}-php- 63 | 64 | - name: Install dependencies 65 | run: composer install --prefer-dist --no-progress 66 | 67 | - name: Run test suite 68 | run: composer run-script test 69 | 70 | build_php_81: 71 | 72 | runs-on: ubuntu-latest 73 | 74 | steps: 75 | - uses: actions/checkout@v2 76 | 77 | - name: Setup PHP with PECL extension 78 | uses: shivammathur/setup-php@v2 79 | with: 80 | php-version: '8.1' 81 | 82 | - name: Validate composer.json and composer.lock 83 | run: composer validate --strict 84 | 85 | - name: Cache Composer packages 86 | id: composer-cache 87 | uses: actions/cache@v2 88 | with: 89 | path: vendor 90 | key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} 91 | restore-keys: | 92 | ${{ runner.os }}-php- 93 | 94 | - name: Install dependencies 95 | run: composer install --prefer-dist --no-progress 96 | 97 | - name: Run test suite 98 | run: composer run-script test 99 | -------------------------------------------------------------------------------- /config/laravel-bitpay.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | $pkPath = 'app' . DIRECTORY_SEPARATOR . 'private' . DIRECTORY_SEPARATOR . 'bitpay' . DIRECTORY_SEPARATOR . 'laravel-bitpay.pk'; 4 | 5 | return [ 6 | /* 7 | * This is the full path and name for the private key. 8 | * The default value is `storage/app/private/bitpay/laravel-bitpay.pk` 9 | */ 10 | 'private_key' => env('BITPAY_PRIVATE_KEY_PATH') ?: storage_path($pkPath), 11 | 12 | /* 13 | * Specifies using the Live Bitcoin network or 14 | * Test Bitcoin network: livenet or testnet. 15 | * 16 | * The default is livenet 17 | */ 18 | 'network' => env('BITPAY_NETWORK', 'livenet'), 19 | 20 | /* 21 | * The key_storage option allows you to specify a class for persisting and retrieving keys. 22 | * 23 | * By default this uses the Bitpay\Storage\EncryptedFilesystemStorage class. 24 | */ 25 | 'key_storage' => \BitPayKeyUtils\Storage\EncryptedFilesystemStorage::class, 26 | 27 | /* 28 | * This is the password used to encrypt and decrypt keys on the filesystem. 29 | */ 30 | 'key_storage_password' => env('BITPAY_KEY_STORAGE_PASSWORD', 'RandomPasswordForEncryption'), 31 | 32 | /* 33 | * Generate/Enable use of BitPay token for 'merchant' facade? 34 | * 35 | * Default: true 36 | */ 37 | 'merchant_facade_enabled' => env('BITPAY_ENABLE_MERCHANT', true), 38 | 39 | /* 40 | * Generate/Enable use of BitPay token for 'payout' facade? 41 | * 42 | * Default: false 43 | */ 44 | 'payout_facade_enabled' => env('BITPAY_ENABLE_PAYOUT', false), 45 | 46 | /* 47 | * BitPay Merchant Token 48 | * 49 | * Default: null 50 | */ 51 | 'merchant_token' => env('BITPAY_MERCHANT_TOKEN'), 52 | 53 | /* 54 | * BitPay Payout Token 55 | * 56 | * Default: null 57 | */ 58 | 'payout_token' => env('BITPAY_PAYOUT_TOKEN'), 59 | 60 | /* 61 | * Indicates if configured webhook (notificationURL) should automatically be set on: 62 | * - Invoices 63 | * - Recipients 64 | * - Payouts/PayoutBatches 65 | * 66 | * This feature is overridden when a value is manually set on a respective resource 67 | * before submitting it to the BitPay API. 68 | * 69 | * Uncomment an entry to enable its auto-population. 70 | */ 71 | 'auto_populate_webhook' => [ 72 | // \Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate::For_Invoices, 73 | // \Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate::For_Recipients, 74 | // \Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate::For_Payouts, 75 | ], 76 | ]; 77 | -------------------------------------------------------------------------------- /src/Actions/ManageSettlements.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use BitPaySDK\Model\Settlement\Settlement; 7 | 8 | 9 | /** 10 | * Settlements are transfers of payment profits from BitPay to bank accounts and cryptocurrency wallets owned by 11 | * merchants, partners, etc. This endpoint exposes reports detailing these settlements. 12 | * 13 | * @link https://bitpay.com/api/#rest-api-resources-settlements-resource 14 | */ 15 | trait ManageSettlements 16 | { 17 | 18 | /** 19 | * Retrieve settlements. 20 | * 21 | * @link https://bitpay.com/api/#rest-api-resources-settlements-retrieve-settlements 22 | * 23 | * @param string $currency The three digit currency string for the ledger to retrieve. 24 | * @param string $startDate The start of the date window to query for settlements. Format YYYY-MM-DD 25 | * @param string $endDate The end of the date window to query for settlements. Format YYYY-MM-DD 26 | * @param string|null $status The settlement status you want to query on. 27 | * Can be `new`, `processing`, `rejected` or `completed`. 28 | * @param int|null $limit Maximum results that the query will return (useful for paging results). 29 | * @param int|null $offset Number of results to offset 30 | * (ex. skip 10 will give you results starting with the 11th result) 31 | * 32 | * @return Settlement[] A list of BitPay Settlement objects. 33 | * @throws BitPayException BitPayException class 34 | */ 35 | public static function getSettlements( 36 | string $currency, 37 | string $startDate, 38 | string $endDate, 39 | string $status = null, 40 | int $limit = null, 41 | int $offset = null 42 | ): array { 43 | return (new self())->client->getSettlements($currency, $startDate, $endDate, $status, $limit, $offset); 44 | } 45 | 46 | /** 47 | * Retrieve a settlement. 48 | * 49 | * @link https://bitpay.com/api/#rest-api-resources-settlements-retrieve-settlements 50 | * 51 | * @param $settlementId string Settlement Id. 52 | * 53 | * @return Settlement A BitPay Settlement object. 54 | * @throws BitPayException BitPayException class 55 | */ 56 | public static function getSettlement(string $settlementId): Settlement 57 | { 58 | return (new self())->client->getSettlement($settlementId); 59 | } 60 | 61 | /** 62 | * Fetch a reconciliation report. Allows merchant to retrieve a detailed report of the activity within 63 | * the settlement period, in order to reconcile incoming settlements from BitPay. 64 | * 65 | * @link https://bitpay.com/api/#rest-api-resources-settlements-fetch-a-reconciliation-report 66 | * 67 | * @param $settlement Settlement to generate report for. 68 | * 69 | * @return Settlement A detailed BitPay Settlement object. 70 | * @throws BitPayException BitPayException class 71 | */ 72 | public static function getSettlementReconciliationReport(Settlement $settlement): Settlement 73 | { 74 | return (new self())->client->getSettlementReconciliationReport($settlement); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Actions/ManageRefunds.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use BitPaySDK\Exceptions\RefundCancellationException; 7 | use BitPaySDK\Model\Invoice\Invoice; 8 | use BitPaySDK\Model\Invoice\Refund; 9 | 10 | 11 | trait ManageRefunds 12 | { 13 | 14 | /** 15 | * Get BitPay refund instance. 16 | * 17 | * @return Refund 18 | */ 19 | public static function Refund(): Refund 20 | { 21 | return new Refund(); 22 | } 23 | 24 | /** 25 | * Create a BitPay refund. 26 | * 27 | * @link https://bitpay.com/api/#rest-api-resources-invoices-refund-an-invoice 28 | * 29 | * @param $invoice Invoice A BitPay invoice object for which a refund request should be made. Must have 30 | * been obtained using the merchant facade. 31 | * @param $refundEmail string The email of the buyer to which the refund email will be sent 32 | * @param $amount float The amount of money to refund. If zero then a request for 100% of the invoice 33 | * value is created. 34 | * @param $currency string The three digit currency code specifying the exchange rate to use when 35 | * calculating the refund bitcoin amount. If this value is "BTC" then no exchange rate 36 | * calculation is performed. 37 | * 38 | * @return bool True if the refund was successfully created, false otherwise. 39 | * @throws BitPayException BitPayException class 40 | */ 41 | public static function createRefund( 42 | Invoice $invoice, 43 | string $refundEmail, 44 | float $amount, 45 | string $currency 46 | ): bool { 47 | return (new self())->client->createRefund($invoice, $refundEmail, $amount, $currency); 48 | } 49 | 50 | /** 51 | * Retrieve all refund requests on a BitPay invoice. 52 | * 53 | * @link https://bitpay.com/api/#rest-api-resources-invoices-retrieve-all-refund-requests-on-an-invoice 54 | * 55 | * @param $invoice Invoice The BitPay invoice having the associated refunds. 56 | * 57 | * @return Refund[] An array of BitPay refund object with the associated Refund object updated. 58 | * @throws BitPayException BitPayException class 59 | */ 60 | public static function getRefunds(Invoice $invoice): array 61 | { 62 | return (new self())->client->getRefunds($invoice); 63 | } 64 | 65 | /** 66 | * Retrieve a previously made refund request on a BitPay invoice. 67 | * 68 | * @link https://bitpay.com/api/#rest-api-resources-invoices-retrieve-a-refund-request 69 | * 70 | * @param $invoice Invoice The BitPay invoice having the associated refund. 71 | * @param $refundId string The refund id for the refund to be updated with new status. 72 | * 73 | * @return Refund A BitPay refund object with the associated Refund object updated. 74 | * @throws BitPayException BitPayException class 75 | */ 76 | public static function getRefund(Invoice $invoice, string $refundId): Refund 77 | { 78 | return (new self())->client->getRefund($invoice, $refundId); 79 | } 80 | 81 | /** 82 | * Cancel a previously submitted refund request on a BitPay invoice. 83 | * 84 | * @link https://bitpay.com/api/#rest-api-resources-invoices-cancel-a-refund-request 85 | * 86 | * @param $invoiceId string The refund id for the refund to be canceled. 87 | * @param $refund Refund The BitPay invoice having the associated refund to be canceled. 88 | * Must have been obtained using the merchant facade. 89 | * 90 | * @return bool True if the refund was successfully canceled, false otherwise. 91 | * @throws RefundCancellationException RefundCancellationException class 92 | */ 93 | public static function cancelRefund(string $invoiceId, Refund $refund): bool 94 | { 95 | return (new self())->client->cancelRefund($invoiceId, $refund); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/Actions/ManageBills.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use BitPaySDK\Model\Bill\Bill; 7 | use BitPaySDK\Model\Bill\Item; 8 | 9 | 10 | /** 11 | * Bills are payment requests addressed to specific buyers. 12 | * Bill line items have fixed prices, typically denominated in fiat currency. 13 | * 14 | * @link https://bitpay.com/api/#rest-api-resources-bills-resource 15 | */ 16 | trait ManageBills 17 | { 18 | /** 19 | * Get BitPay Bill instance. 20 | * 21 | * @param string|null $number A bill number for tracking purposes. 22 | * @param string|null $currency The three digit currency code used to compute the bill's crypto amount. 23 | * @param string|null $email The email address of the receiver for this bill. 24 | * @param Item[]|null $items The list of BillItems to add to this bill. 25 | * 26 | * @return Bill 27 | */ 28 | public static function Bill( 29 | string $number = null, 30 | string $currency = null, 31 | string $email = null, 32 | array $items = null): Bill 33 | { 34 | return new Bill($number, $currency, $email, $items); 35 | } 36 | 37 | /** 38 | * Get BitPay Bill Item instance. 39 | * 40 | * @return Item A BitPay Bill Item 41 | */ 42 | public static function BillItem(): Item 43 | { 44 | return new Item(); 45 | } 46 | 47 | /** 48 | * Create a BitPay Bill. 49 | * 50 | * @link https://bitpay.com/api/#rest-api-resources-bills-create-a-bill 51 | * 52 | * @param $bill Bill A Bill object with request parameters defined. 53 | * 54 | * @return Bill A BitPay generated Bill object. 55 | * @throws BitPayException BitPayException class 56 | */ 57 | public static function createBill(Bill $bill): Bill 58 | { 59 | return (new self())->client->createBill($bill); 60 | } 61 | 62 | /** 63 | * Retrieve a BitPay bill by its id. 64 | * 65 | * @link https://bitpay.com/api/#rest-api-resources-bills-retrieve-a-bill 66 | * 67 | * @param $billId string The id of the bill to retrieve. 68 | * 69 | * @return Bill A BitPay Bill object. 70 | * @throws BitPayException BitPayException class 71 | */ 72 | public static function getBill(string $billId): Bill 73 | { 74 | return (new self())->client->getBill($billId); 75 | } 76 | 77 | /** 78 | * Retrieve a collection of BitPay bills. 79 | * 80 | * @link https://bitpay.com/api/#rest-api-resources-bills-retrieve-bills-by-status 81 | * 82 | * @param $status string|null The status to filter the bills. 83 | * 84 | * @return Bill[] A list of BitPay Bill objects. 85 | * @throws BitPayException BitPayException class 86 | */ 87 | public static function getBills(string $status = null): array 88 | { 89 | return (new self())->client->getBills($status); 90 | } 91 | 92 | /** 93 | * Update a BitPay Bill. 94 | * 95 | * @link https://bitpay.com/api/#rest-api-resources-bills-update-a-bill 96 | * 97 | * @param $bill Bill A Bill object with the parameters to update defined. 98 | * @param $billId string The ID of the Bill to update. 99 | * 100 | * @return Bill An updated Bill object. 101 | * @throws BitPayException BitPayException class 102 | */ 103 | public static function updateBill(Bill $bill, string $billId): Bill 104 | { 105 | return (new self())->client->updateBill($bill, $billId); 106 | } 107 | 108 | /** 109 | * Deliver a BitPay Bill. 110 | * 111 | * @link https://bitpay.com/api/#rest-api-resources-bills-deliver-a-bill-via-email 112 | * 113 | * @param $billId string The id of the requested bill. 114 | * @param $billToken string The token of the requested bill. 115 | * 116 | * @return bool True if the bill has been delivered, false otherwise. 117 | * @throws BitPayException BitPayException class 118 | */ 119 | public static function deliverBill(string $billId, string $billToken): bool 120 | { 121 | return strtolower((new self())->client->deliverBill($billId, $billToken)) === "success"; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Traits/CreateKeypairTrait.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Traits; 4 | 5 | use Vrajroham\LaravelBitpay\Exceptions\InvalidConfigurationException; 6 | 7 | 8 | trait CreateKeypairTrait 9 | { 10 | public function validateAndLoadConfig() 11 | { 12 | $config = config('laravel-bitpay'); 13 | 14 | if ('livenet' !== $config['network'] && 'testnet' !== $config['network']) { 15 | throw InvalidConfigurationException::invalidNetworkName(); 16 | } 17 | 18 | if (! class_exists($config['key_storage'])) { 19 | throw InvalidConfigurationException::invalidStorageClass(); 20 | } 21 | 22 | if ('' === trim($config['key_storage_password'])) { 23 | throw InvalidConfigurationException::invalidOrEmptyPassword(); 24 | } 25 | 26 | $this->config = $config; 27 | } 28 | 29 | protected function getEnabledFacades(): array 30 | { 31 | $facades = []; 32 | 33 | if (! empty($this->config['merchant_facade_enabled']) && $this->config['merchant_facade_enabled']) { 34 | $facades[] = 'merchant'; 35 | } 36 | 37 | if (! empty($this->config['payout_facade_enabled']) && $this->config['payout_facade_enabled']) { 38 | $facades[] = 'payout'; 39 | } 40 | 41 | return $facades; 42 | } 43 | 44 | /** 45 | * 46 | * @param string $facade One of 'merchant' or 'payout' 47 | * 48 | * @throws \Exception 49 | */ 50 | private function getEnvReplacementString(string $facade): string 51 | { 52 | if ($facade === 'merchant') { 53 | return "BITPAY_MERCHANT_TOKEN"; 54 | } elseif ($facade === 'payout') { 55 | return "BITPAY_PAYOUT_TOKEN"; 56 | } 57 | 58 | throw new \Exception("'$facade' is not a valid BitPay facade!", 1); 59 | } 60 | 61 | /** 62 | * Write facade-specific token to .env 63 | * Update it if it exists, otherwise add it below existing BITPAY_* entries. 64 | * Finally, and very unlikely, if it doesn't exist nor does any other BITPAY_* entries, write it to the end 65 | * of the .env 66 | * 67 | * @param string $facade One of 'merchant' or 'payout' 68 | * 69 | * @throws \Exception 70 | */ 71 | protected function writeNewEnvironmentFileWith(string $facade) 72 | { 73 | $replString = $this->getEnvReplacementString($facade) . '=' . $this->token; 74 | $envFilePath = $this->laravel->environmentFilePath(); 75 | $envFileContents = file_get_contents($envFilePath); 76 | 77 | if (strpos($envFileContents, $this->getEnvReplacementString($facade)) !== false) { 78 | file_put_contents($envFilePath, preg_replace( 79 | $this->keyReplacementPattern($facade), 80 | $replString, 81 | $envFileContents) 82 | ); 83 | } else { 84 | $envLines = file($envFilePath, FILE_IGNORE_NEW_LINES); 85 | $offset = null; 86 | 87 | foreach ($envLines as $line) { 88 | if (strpos($line, "BITPAY_") === 0) { 89 | $offset = array_search($line, $envLines); 90 | } 91 | } 92 | 93 | // Highly unlikely, but place token at end of .env if no other BITPAY_* entries exist 94 | if ($offset === null) { 95 | $offset = count($envLines) - 1; 96 | $replString = "\n" . $replString; 97 | } 98 | 99 | array_splice($envLines, $offset + 1, 0, $replString); 100 | file_put_contents($envFilePath, implode("\n", $envLines)); 101 | } 102 | } 103 | 104 | /** 105 | * 106 | * @param string $facade One of 'merchant' or 'payout' 107 | * 108 | * @throws \Exception 109 | */ 110 | protected function keyReplacementPattern(string $facade): string 111 | { 112 | if ($facade === 'merchant') { 113 | $token = $this->laravel['config']['laravel-bitpay.merchant_token']; 114 | } elseif ($facade === 'payout') { 115 | $token = $this->laravel['config']['laravel-bitpay.payout_token']; 116 | } else { 117 | throw new \Exception("'$facade' is not a valid BitPay facade!", 1); 118 | } 119 | 120 | $replString = $this->getEnvReplacementString($facade); 121 | $escaped = preg_quote('=' . $token, '/'); 122 | 123 | return "/^" . $replString . "{$escaped}/m"; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Actions/ManageSubscriptions.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Model\Subscription\BillData; 6 | use BitPaySDK\Model\Subscription\Item; 7 | use BitPaySDK\Model\Subscription\Subscription; 8 | 9 | 10 | trait ManageSubscriptions 11 | { 12 | /** 13 | * Subscriptions are repeat billing agreements with specific buyers. 14 | * BitPay sends bill emails to buyers identified in active subscriptions according to the specified schedule. 15 | * 16 | * @link https://bitpay.com/api/#rest-api-resources-subscriptions 17 | * @return Subscription 18 | */ 19 | public static function Subscription(): Subscription 20 | { 21 | return new Subscription(); 22 | } 23 | 24 | /** 25 | * @param float $price Line item unit price for the corresponding Subscription's currency 26 | * @param int $quantity Line item number of units 27 | * @param string $description Line item description 28 | * 29 | * @return Item A BitPay Subscription Item 30 | */ 31 | public static function SubscriptionItem(float $price = 0.0, int $quantity = 0, string $description = ""): Item 32 | { 33 | return new Item($price, $quantity, $description); 34 | } 35 | 36 | /** 37 | * @param string $currency ISO 4217 3-character currency code. 38 | * This is the currency associated with the items' price field. 39 | * @param string $email Subscription recipient's email address 40 | * @param string $dueDate Date and time at which a bill is due, ISO-8601 format yyyy-mm-ddThh:mm:ssZ (UTC). 41 | * @param array $items Array of line items 42 | * 43 | * @return BillData A BitPay Subscription's billData 44 | */ 45 | public static function BillData(string $currency, string $email, string $dueDate, array $items): BillData 46 | { 47 | return new BillData($currency, $email, $dueDate, $items); 48 | } 49 | 50 | /** 51 | * Create a BitPay Subscription. 52 | * 53 | * @link https://bitpay.com/api/#rest-api-resources-subscriptions-create-a-subscription 54 | * 55 | * @param $subscription Subscription A Subscription object with request parameters defined. 56 | * 57 | * @return Subscription A BitPay generated Subscription object. 58 | * @throws \BitPaySDK\Exceptions\BitPayException BitPayException class 59 | */ 60 | public static function createSubscription(Subscription $subscription): Subscription 61 | { 62 | return (new self())->client->createSubscription($subscription); 63 | } 64 | 65 | /** 66 | * Retrieve a BitPay subscription by its id. 67 | * 68 | * @link https://bitpay.com/api/#rest-api-resources-subscriptions-retrieve-a-subscription 69 | * 70 | * @param $subscriptionId string The id of the subscription to retrieve. 71 | * 72 | * @return Subscription A BitPay Subscription object. 73 | * @throws \BitPaySDK\Exceptions\BitPayException BitPayException class 74 | */ 75 | public static function getSubscription(string $subscriptionId): Subscription 76 | { 77 | return (new self())->client->getSubscription($subscriptionId); 78 | } 79 | 80 | /** 81 | * Retrieve a collection of BitPay subscriptions. 82 | * 83 | * @link https://bitpay.com/api/#rest-api-resources-subscriptions-retrieve-subscriptions-based-on-status 84 | * 85 | * @param $status string|null The status to filter the subscriptions. 86 | * 87 | * @return Subscription[] A list of BitPay Subscription objects. 88 | * @throws \BitPaySDK\Exceptions\BitPayException BitPayException class 89 | */ 90 | public static function getSubscriptions(string $status = null): array 91 | { 92 | return (new self())->client->getSubscriptions($status); 93 | } 94 | 95 | /** 96 | * Update a BitPay Subscription. 97 | * 98 | * @link https://bitpay.com/api/#rest-api-resources-subscriptions-update-a-subscription 99 | * 100 | * @param $subscription Subscription A Subscription object with the parameters to update defined. 101 | * @param $subscriptionId string $subscriptionIdThe Id of the Subscription to update. 102 | * 103 | * @return Subscription An updated Subscription object. 104 | * @throws \BitPaySDK\Exceptions\BitPayException BitPayException class 105 | */ 106 | public static function updateSubscription(Subscription $subscription, string $subscriptionId): Subscription 107 | { 108 | return (new self())->client->updateSubscription($subscription, $subscriptionId); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Actions/ManageInvoices.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use BitPaySDK\Model\Invoice\Buyer; 7 | use BitPaySDK\Model\Invoice\Invoice; 8 | use Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate; 9 | 10 | 11 | trait ManageInvoices 12 | { 13 | /** 14 | * Get BitPay Invoice instance. 15 | * 16 | * @link https://bitpay.com/api/#rest-api-resources-invoices-resource 17 | * 18 | * @param float|null $price float The amount for which the invoice will be created. 19 | * @param string|null $currency string three digit currency code used to compute the invoice bitcoin amount. 20 | * 21 | * @return Invoice 22 | */ 23 | public static function Invoice(float $price = null, string $currency = null): Invoice 24 | { 25 | return new Invoice($price, $currency); 26 | } 27 | 28 | /** 29 | * @return Buyer 30 | */ 31 | public static function Buyer(): Buyer 32 | { 33 | return new Buyer(); 34 | } 35 | 36 | /** 37 | * Create a BitPay invoice. 38 | * 39 | * @link https://bitpay.com/api/#rest-api-resources-invoices-create-an-invoice 40 | * 41 | * @param $invoice Invoice An Invoice object with request parameters defined. 42 | * 43 | * @return Invoice $invoice A BitPay generated Invoice object. 44 | * @throws BitPayException BitPayException class 45 | */ 46 | public static function createInvoice(Invoice $invoice): Invoice 47 | { 48 | $thisInstance = new self(); 49 | 50 | try { 51 | if (empty($invoice->getNotificationURL()) && 52 | in_array(WebhookAutoPopulate::For_Invoices, $thisInstance->config['auto_populate_webhook'])) { 53 | $invoice->setNotificationURL(route('laravel-bitpay.webhook.capture')); 54 | } 55 | } catch (\Throwable $exception) { 56 | // Misconfiguration or route macro not in use 57 | } 58 | 59 | return $thisInstance->client->createInvoice($invoice); 60 | } 61 | 62 | /** 63 | * Retrieve a BitPay invoice by its id. 64 | * 65 | * @link https://bitpay.com/api/#rest-api-resources-invoices-retrieve-an-invoice 66 | * 67 | * @param $invoiceId string The id of the invoice to retrieve. 68 | * 69 | * @return Invoice A BitPay Invoice object. 70 | * @throws BitPayException BitPayException class 71 | */ 72 | public static function getInvoice(string $invoiceId): Invoice 73 | { 74 | return (new self())->client->getInvoice($invoiceId); 75 | } 76 | 77 | /** 78 | * Retrieve a collection of BitPay invoices. 79 | * 80 | * @link https://bitpay.com/api/#rest-api-resources-invoices-retrieve-invoices-filtered-by-query 81 | * 82 | * @param $dateStart string The start of the date window to query for invoices. Format YYYY-MM-DD. 83 | * @param $dateEnd string The end of the date window to query for invoices. Format YYYY-MM-DD. 84 | * @param $status string|null The invoice status you want to query on. 85 | * @param $orderId string|null The optional order id specified at time of invoice creation. 86 | * @param $limit int|null Maximum results that the query will return (useful for paging results). 87 | * @param $offset int|null Number of results to offset (ex. skip 10 will give you results starting with the 11th result). 88 | * 89 | * @return Invoice[] A list of BitPay Invoice objects. 90 | * @throws BitPayException BitPayException class 91 | */ 92 | public static function getInvoices( 93 | string $dateStart, 94 | string $dateEnd, 95 | string $status = null, 96 | string $orderId = null, 97 | int $limit = null, 98 | int $offset = null 99 | ): array { 100 | return (new self())->client->getInvoices($dateStart, $dateEnd, $status, $orderId, $limit, $offset); 101 | } 102 | 103 | /** 104 | * Request the last BitPay Invoice webhook to be resent. 105 | * 106 | * @link https://bitpay.com/api/#rest-api-resources-invoices-request-a-webhook-to-be-resent 107 | * 108 | * @param string $invoiceId The id of the invoice for which you want the last webhook to be resent. 109 | * 110 | * @return bool True if the webhook has been resent for the current invoice status, false otherwise. 111 | * @throws \BitPaySDK\Exceptions\BitPayException BitPayException class 112 | */ 113 | public static function requestInvoiceWebhook(string $invoiceId): bool 114 | { 115 | return (new self())->client->requestInvoiceNotification($invoiceId); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Actions/ManageRecipients.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\BitPayException; 6 | use BitPaySDK\Exceptions\PayoutQueryException; 7 | use BitPaySDK\Exceptions\PayoutRecipientCancellationException; 8 | use BitPaySDK\Exceptions\PayoutRecipientCreationException; 9 | use BitPaySDK\Exceptions\PayoutRecipientNotificationException; 10 | use BitPaySDK\Exceptions\PayoutRecipientUpdateException; 11 | use BitPaySDK\Model\Payout\PayoutRecipient; 12 | use BitPaySDK\Model\Payout\PayoutRecipients; 13 | use Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate; 14 | 15 | 16 | /** 17 | * To create payout requests, merchants will first need to issue email invites using this resource. 18 | * This is a mandatory step to onboard customers asking for cryptocurrency payouts. 19 | * The recipients of the email invites will be invited to create a BitPay personal account, 20 | * submit a photo of a proof of ID document (Passport, driver's license, Identity card) and provide 21 | * the home address in order to be able to submit a cryptocurrency withdrawal address to be used for the payouts. 22 | * 23 | * @link https://bitpay.com/api/#rest-api-resources-recipients 24 | */ 25 | trait ManageRecipients 26 | { 27 | 28 | /** 29 | * The Recipient resource allows a merchant to invite his clients to signup for a BitPay personal account. 30 | * 31 | * @link https://bitpay.com/api/#rest-api-resources-recipients-resource 32 | * 33 | * @param string|null $email Recipient email address 34 | * @param string|null $label For merchant use, pass through - could be customer name or unique reference. 35 | * @param string|null $notificationURL URL to which BitPay sends webhook notifications to inform the merchant about 36 | * the status of a given recipient. HTTPS is mandatory. 37 | * 38 | * @return PayoutRecipient 39 | */ 40 | public static function PayoutRecipient( 41 | string $email = null, 42 | string $label = null, 43 | string $notificationURL = null 44 | ): PayoutRecipient { 45 | return new PayoutRecipient($email, $label, $notificationURL); 46 | } 47 | 48 | /** 49 | * A PayoutRecipients object, used primarily when merchants invite their payees. 50 | * 51 | * @param PayoutRecipient[] $recipients An array of PayoutRecipient objects 52 | * 53 | * @return PayoutRecipients 54 | */ 55 | public static function PayoutRecipients(array $recipients = []): PayoutRecipients 56 | { 57 | return new PayoutRecipients($recipients); 58 | } 59 | 60 | /** 61 | * Invite Recipient(s). 62 | * 63 | * For merchants who need to invite multiple recipients in a short period of time, make sure to send batch of 64 | * invites e.g. use this endpoint to invite an array [] of recipients (up to 1000 in a single API call) 65 | * 66 | * By default, a merchant can invite a maximum of 1000 distinct recipients via the business account, 67 | * reach out to your account manager at BitPay in order to increase this limit. 68 | * 69 | * @link https://bitpay.com/api/#rest-api-resources-recipients-invite-a-recipient 70 | * 71 | * @param $recipients PayoutRecipients A PayoutRecipients object with one or more PayoutRecipient included. 72 | * 73 | * @return PayoutRecipient[] A list of BitPay PayoutRecipient objects. 74 | * @throws PayoutRecipientCreationException BitPayException class 75 | */ 76 | public static function invitePayoutRecipients(PayoutRecipients $recipients): array 77 | { 78 | $thisInstance = new self(); 79 | 80 | try { 81 | foreach ($recipients->getRecipients() as $recipient) { 82 | if (empty($recipient['notificationURL']) && 83 | in_array(WebhookAutoPopulate::For_Recipients, $thisInstance->config['auto_populate_webhook'])) { 84 | $recipient['notificationURL'] = route('laravel-bitpay.webhook.capture'); 85 | } 86 | } 87 | } catch (\Throwable $exception) { 88 | // Misconfiguration or route macro not in use 89 | } 90 | 91 | return $thisInstance->client->submitPayoutRecipients($recipients); 92 | } 93 | 94 | /** 95 | * Retrieve a BitPay payout recipient by its ID. 96 | * 97 | * @link https://bitpay.com/api/#rest-api-resources-recipients-retrieve-a-recipient 98 | * 99 | * @param $recipientId string The id of the recipient to retrieve. 100 | * 101 | * @return PayoutRecipient A BitPay PayoutRecipient object. 102 | * @throws PayoutQueryException BitPayException class 103 | */ 104 | public static function getPayoutRecipient(string $recipientId): PayoutRecipient 105 | { 106 | return (new self())->client->getPayoutRecipient($recipientId); 107 | } 108 | 109 | /** 110 | * Retrieve recipients by status. 111 | * 112 | * @link https://bitpay.com/api/#rest-api-resources-recipients-retrieve-recipients-by-status 113 | * 114 | * @param $status string|null The recipient status you want to query on. 115 | * @param $limit int|null Maximum results that the query will return (useful for paging results). 116 | * @param $offset int|null Number of results to offset (ex. skip 10 will give you results starting with the 11th result). 117 | * 118 | * @return PayoutRecipient[] A list of BitPay PayoutRecipient objects. 119 | * @throws BitPayException BitPayException class 120 | */ 121 | public static function getPayoutRecipients(string $status = null, int $limit = null, int $offset = null): array 122 | { 123 | return (new self())->client->getPayoutRecipients($status, $limit, $offset); 124 | } 125 | 126 | /** 127 | * Update a Recipient. 128 | * 129 | * @link https://bitpay.com/api/#rest-api-resources-recipients-update-a-recipient 130 | * 131 | * @param $recipientId string The ID for the recipient to be updated. 132 | * @param $recipient PayoutRecipient A PayoutRecipient object with updated parameters defined. 133 | * 134 | * @return PayoutRecipient The updated PayoutRecipient object. 135 | * @throws PayoutRecipientUpdateException PayoutRecipientUpdateException class 136 | */ 137 | public static function updatePayoutRecipient(string $recipientId, PayoutRecipient $recipient): PayoutRecipient 138 | { 139 | return (new self())->client->updatePayoutRecipient($recipientId, $recipient); 140 | } 141 | 142 | /** 143 | * Remove a recipient 144 | * 145 | * @link https://bitpay.com/api/#rest-api-resources-recipients-remove-a-recipient 146 | * 147 | * @param $recipientId string The ID of the recipient to be removed. 148 | * 149 | * @return bool True if the recipient was successfully removed, false otherwise. 150 | * @throws PayoutRecipientCancellationException PayoutRecipientCancellationException class 151 | */ 152 | public static function removePayoutRecipient(string $recipientId): bool 153 | { 154 | return (new self())->client->deletePayoutRecipient($recipientId); 155 | } 156 | 157 | /** 158 | * Request a Recipient webhook to be resent. 159 | * 160 | * @link https://bitpay.com/api/#rest-api-resources-recipients-request-a-webhook-to-be-resent 161 | * 162 | * @param $recipientId string The id of the recipient for which you want the last webhook to be resent. 163 | * 164 | * @return bool True if the webhook has been resent for the current recipient status, false otherwise. 165 | * @throws PayoutRecipientNotificationException PayoutRecipientNotificationException class 166 | */ 167 | public static function requestPayoutRecipientWebhook(string $recipientId): bool 168 | { 169 | return (new self())->client->requestPayoutRecipientNotification($recipientId); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/Console/CreateKeypairCommand.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Console; 4 | 5 | use BitPayKeyUtils\KeyHelper\PrivateKey; 6 | use GuzzleHttp\Client; 7 | use Illuminate\Console\Command; 8 | use Illuminate\Support\Str; 9 | use Symfony\Component\Console\Input\InputOption; 10 | use Vrajroham\LaravelBitpay\Traits\CreateKeypairTrait; 11 | 12 | 13 | class CreateKeypairCommand extends Command 14 | { 15 | private $config; 16 | private $privateKey; 17 | private $publicKey; 18 | private $storageEngine; 19 | private $client; 20 | private $network; 21 | private $adapter; 22 | private $sin; 23 | private $tokenLabel; 24 | private $token; 25 | private $pairingCode; 26 | private $pairingExpiration; 27 | private $approveLink; 28 | private $bar; 29 | private const HEADER_FOOTER_WIDTH = 50; 30 | use CreateKeypairTrait; 31 | 32 | 33 | /** 34 | * The name and signature of the console command. 35 | * 36 | * @var string 37 | */ 38 | protected $signature = 'laravel-bitpay:createkeypair'; 39 | 40 | /** 41 | * The console command description. 42 | * 43 | * @var string 44 | */ 45 | protected $description = 'Create and persist keypair(s). Pair client with BitPay server.'; 46 | 47 | /** 48 | * Create a new command instance. 49 | */ 50 | public function __construct() 51 | { 52 | parent::__construct(); 53 | 54 | $this->addOption('fresh', 'f', InputOption::VALUE_NONE, 'Explicitly generate a ' . 55 | 'fresh private key. By default, if a (valid) key exists, it will be used instead.'); 56 | } 57 | 58 | /** 59 | * @inheritDoc 60 | * Polyfill which enables compatibility with Laravel <8.x 61 | * 62 | * @param int $count 63 | * 64 | * @return void 65 | * @since 5.0.1 66 | */ 67 | public function newLine($count = 1) 68 | { 69 | $this->output->newLine($count); 70 | } 71 | 72 | /** 73 | * Advance progress bar by $steps, and write 'info' level $message to console, optionally skipping by $skipSteps. 74 | * 75 | * @param string $message 76 | * @param int $steps 77 | * @param int $skipSteps 78 | */ 79 | private function advanceWithInfo(string $message, int $steps = 1, int $skipSteps = 0) 80 | { 81 | $this->bar->clear(); 82 | if ($skipSteps > 0) { 83 | $this->bar->setMaxSteps($this->bar->getMaxSteps() - $skipSteps); 84 | } 85 | $this->bar->advance($steps); 86 | $this->info($message); 87 | } 88 | 89 | private function sectionHeader(string $sectionTitle) 90 | { 91 | $diffWidth = self::HEADER_FOOTER_WIDTH - (strlen($sectionTitle) + 2); 92 | $borderWidth = round($diffWidth / 2, 0, PHP_ROUND_HALF_DOWN); 93 | 94 | $this->info('<fg=magenta>' . str_repeat('#', $borderWidth) . ' ' 95 | . $sectionTitle . ' ' . str_repeat('#', $borderWidth) . '</>'); 96 | $this->newLine(); 97 | } 98 | 99 | private function sectionFooter() 100 | { 101 | $this->newLine(); 102 | $this->info('<fg=magenta>' . str_repeat('#', self::HEADER_FOOTER_WIDTH) . '</>'); 103 | $this->newLine(2); 104 | } 105 | 106 | /** 107 | * Execute the console command. 108 | */ 109 | public function handle() 110 | { 111 | try { 112 | $this->validateAndLoadConfig(); 113 | 114 | $enabled_facades = $this->getEnabledFacades(); 115 | 116 | if (empty($enabled_facades)) { 117 | $error_message = 'No facades enabled for token generation! Open your <options=bold,underscore>.env</>' . 118 | ' file and set values for <options=bold,underscore>BITPAY_ENABLE_MERCHANT</> and/or ' . 119 | '<options=bold,underscore>BITPAY_ENABLE_PAYOUT</>'; 120 | $this->error($error_message); 121 | exit(1); 122 | } 123 | 124 | // Each facade effects 2 progress advances and keypair loading/generation effects a max of 5. 125 | $this->bar = $this->output->createProgressBar((count($enabled_facades) * 2) + 5); 126 | 127 | $this->bar->setProgressCharacter('⚡'); 128 | $this->bar->setBarCharacter('-'); 129 | $this->bar->setEmptyBarCharacter(' '); 130 | $this->bar->minSecondsBetweenRedraws(0); 131 | 132 | $this->newLine(); 133 | 134 | $this->createAndPersistKeypair(); 135 | 136 | foreach ($enabled_facades as $facade) { 137 | $this->sectionHeader(strtoupper($facade) . ' FACADE'); 138 | 139 | $this->pairWithServerAndCreateToken($facade); 140 | 141 | $this->writeNewEnvironmentFileWith($facade); 142 | 143 | // Convert to IF-ELSE construct if BitPay ever use additional facades. if, if, iffity if. 144 | $this->laravel['config']['laravel-bitpay.' . 145 | ($facade === 'merchant' ? 'merchant_' : ($facade === 'payout' ? 'payout_' : '')) . 'token'] = $this->token; 146 | 147 | $this->newLine(); 148 | 149 | $this->line('<options=bold,underscore>Token Label</> : <options=bold;fg=bright-cyan>' . $this->tokenLabel . '</>'); 150 | 151 | $this->line('<options=bold,underscore>Token</> : <options=bold;fg=bright-cyan>' . $this->token 152 | . '</> (Copied to <options=bold;fg=gray>.env</> for your convenience)'); 153 | 154 | $this->line('<options=bold,underscore>Pairing Code</>: <options=bold;fg=bright-cyan>' . $this->pairingCode 155 | . '</> (Expires: <options=bold;fg=bright-red>' . $this->pairingExpiration . '</>)'); 156 | 157 | $this->newLine(); 158 | 159 | $this->line('Approve your API Token by visiting: <fg=blue;href=' . $this->approveLink . '>' . $this->approveLink . '</>'); 160 | 161 | $this->sectionFooter(); 162 | } 163 | } catch (\Throwable $exception) { 164 | $this->error('Whoops! We have a problem: ' . $exception->getMessage()); 165 | exit(1); 166 | } 167 | } 168 | 169 | /** 170 | * @throws \Exception 171 | */ 172 | private function generateFreshKeyPair() 173 | { 174 | $this->advanceWithInfo(' 🔑 - Generating private key.'); 175 | 176 | $this->privateKey = new PrivateKey($this->config['private_key']); 177 | $this->privateKey->generate(); 178 | 179 | $this->advanceWithInfo(' 🌐 - Generating public key.'); 180 | 181 | $this->publicKey = $this->privateKey->getPublicKey(); 182 | 183 | $this->advanceWithInfo(' 🧰 - Using <options=bold;fg=gray>' . get_class($this->storageEngine) . '</> for secure storage.'); 184 | 185 | $this->storageEngine->persist($this->privateKey); 186 | 187 | $this->advanceWithInfo(' 🔐 - Private key stored securely.'); 188 | } 189 | 190 | /** 191 | * Create private key and public keypair and store in secure file storage, using an existing (valid) keypair by 192 | * default. 193 | * 194 | * 195 | * @throws \Exception 196 | */ 197 | public function createAndPersistKeypair() 198 | { 199 | $this->sectionHeader('KEYPAIR GENERATION'); 200 | 201 | if (in_array('__construct', get_class_methods($this->config['key_storage']))) { 202 | $this->storageEngine = new $this->config['key_storage']($this->config['key_storage_password']); 203 | } else { 204 | $this->storageEngine = new $this->config['key_storage'](); 205 | } 206 | 207 | if ($this->option('fresh')) { 208 | $this->generateFreshKeyPair(); 209 | } else { 210 | try { 211 | // Try to load the configured Private Key, expect it to be missing or corrupted 212 | $this->privateKey = $this->storageEngine->load($this->config['private_key']); 213 | 214 | // Private Key may be uncorrupted but has invalid hexits or decimals 215 | if ($this->privateKey->isValid()) { 216 | $this->publicKey = $this->privateKey->getPublicKey(); 217 | 218 | $this->advanceWithInfo(' 🗝️ - Using existing private key.', 1, 3); 219 | } else { 220 | $this->generateFreshKeyPair(); 221 | } 222 | } catch (\Throwable $exception) { 223 | $this->generateFreshKeyPair(); 224 | } 225 | } 226 | 227 | $this->sin = $this->publicKey->getSin()->__toString(); 228 | 229 | $this->advanceWithInfo(' 🆔 - Created Service Identification Number (SIN Key) for client.'); 230 | 231 | $this->sectionFooter(); 232 | } 233 | 234 | /** 235 | * Initiates client-server pairing. Create token and pairing code on BitPay server for provided facade. 236 | * 237 | * @param string $facade One of 'merchant' or 'payout' 238 | * 239 | * @throws \GuzzleHttp\Exception\GuzzleException 240 | */ 241 | public function pairWithServerAndCreateToken(string $facade) 242 | { 243 | if ('testnet' === $this->config['network']) { 244 | $this->network = 'https://test.bitpay.com'; 245 | } elseif ('livenet' === $this->config['network']) { 246 | $this->network = 'https://bitpay.com'; 247 | } else { 248 | $this->network = 'https://bitpay.com'; 249 | } 250 | 251 | $bitpayClient = new Client(['base_uri' => $this->network]); 252 | 253 | $this->advanceWithInfo(" 🖥️ - Connecting to BitPay server and generating $facade token."); 254 | 255 | // Token label is limited to 60 characters 256 | $this->tokenLabel = Str::substr(ucwords(str_replace(" ", "-", config('app.name'))), 0, 36) 257 | . '__' . Str::ucfirst($facade) . '__' . date('h-i-s_A'); 258 | 259 | $postData = [ 260 | 'id' => (string)$this->sin, 261 | 'label' => $this->tokenLabel, 262 | 'facade' => $facade, 263 | ]; 264 | $response = $bitpayClient->post('/tokens', [ 265 | 'json' => $postData, 266 | 'headers' => [ 267 | 'x-accept-version' => '2.0.0', 268 | 'Content-Type' => 'application/json', 269 | ], 270 | ]); 271 | $response = json_decode($response->getBody()->getContents()); 272 | 273 | $this->advanceWithInfo(" 🥳 - New $facade token and pairing code received from BitPay server."); 274 | 275 | $this->token = $response->data[0]->token; 276 | $this->pairingCode = $response->data[0]->pairingCode; 277 | $this->pairingExpiration = date('M j, Y h:i:s A', intval($response->data[0]->pairingExpiration) / 1000); 278 | 279 | $this->approveLink = $this->network . '/api-access-request?' . 280 | http_build_query(['pairingCode' => $this->pairingCode]); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/Actions/ManagePayouts.php: -------------------------------------------------------------------------------- 1 | <?php 2 | 3 | namespace Vrajroham\LaravelBitpay\Actions; 4 | 5 | use BitPaySDK\Exceptions\PayoutBatchCancellationException; 6 | use BitPaySDK\Exceptions\PayoutBatchCreationException; 7 | use BitPaySDK\Exceptions\PayoutBatchNotificationException; 8 | use BitPaySDK\Exceptions\PayoutBatchQueryException; 9 | use BitPaySDK\Exceptions\PayoutCancellationException; 10 | use BitPaySDK\Exceptions\PayoutCreationException; 11 | use BitPaySDK\Exceptions\PayoutNotificationException; 12 | use BitPaySDK\Exceptions\PayoutQueryException; 13 | use BitPaySDK\Model\Payout\Payout; 14 | use BitPaySDK\Model\Payout\PayoutBatch; 15 | use BitPaySDK\Model\Payout\PayoutInstruction; 16 | use Vrajroham\LaravelBitpay\Constants\WebhookAutoPopulate; 17 | 18 | 19 | /** 20 | * Payouts are batches of bitcoin payments to employees, customers, partners, etc. 21 | * 22 | * @link https://bitpay.com/api/#rest-api-resources-payouts 23 | */ 24 | trait ManagePayouts 25 | { 26 | /** 27 | * Get BitPay Payout instance. 28 | * 29 | * 30 | * @param float|null $amount The total amount of the payout in fiat currency. This amount must equal the 31 | * sum of the instruction's amounts 32 | * @param string|null $currency Currency code set for the payout amount (ISO 4217 3-character currency code). 33 | * Supported currency codes for payouts are EUR, USD, GBP, CAD, NZD, AUD, ZAR 34 | * @param string|null $ledgerCurrency Ledger currency code set for the payout request (ISO 4217 3-character 35 | * currency code), it indicates on which ledger the payout request will be 36 | * recorded. If not provided in the request, this parameter will be set by 37 | * default to the active ledger currency on your account, e.g. your settlement 38 | * currency. 39 | * 40 | * @return Payout 41 | */ 42 | public static function Payout(float $amount = null, string $currency = null, string $ledgerCurrency = null): Payout 43 | { 44 | return new Payout($amount, $currency, $ledgerCurrency); 45 | } 46 | 47 | /** 48 | * Get BitPay PayoutBatch instance. 49 | * 50 | * 51 | * @param string|null $currency Currency code set for the batch amount (ISO 4217 3-character currency code). 52 | * Supported currency codes for payout batches are EUR, USD, GBP, CAD, NZD, AUD, 53 | * ZAR 54 | * @param array|null $instructions An array containing the detailed payout instruction objects. 55 | * This array can contain a maximum of 200 instructions. 56 | * @param string|null $ledgerCurrency Ledger currency code set for the payout request (ISO 4217 3-character 57 | * currency code), it indicates on which ledger the payout request will be 58 | * recorded. If not provided in the request, this parameter will be set by 59 | * default to the active ledger currency on your account, e.g. your settlement 60 | * currency. 61 | * 62 | * @return PayoutBatch 63 | */ 64 | public static function PayoutBatch(string $currency = null, array $instructions = null, string $ledgerCurrency = null): PayoutBatch 65 | { 66 | return new PayoutBatch($currency, $instructions, $ledgerCurrency); 67 | } 68 | 69 | /** 70 | * Get BitPay PayoutInstruction instance. 71 | * 72 | * @param $amount float BTC amount. 73 | * @param $method int Method used to target the recipient. 74 | * @param $methodValue string value for the chosen target method. 75 | * 76 | * @throws PayoutBatchCreationException BitPayException class 77 | * @return PayoutInstruction 78 | */ 79 | public static function PayoutInstruction(float $amount, int $method, string $methodValue): PayoutInstruction 80 | { 81 | return new PayoutInstruction($amount, $method, $methodValue); 82 | } 83 | 84 | /** 85 | * Create a payout. 86 | * 87 | * @param Payout $payout A Payout object with request parameters defined. 88 | * 89 | * @return Payout A BitPay generated Payout object. 90 | * @throws PayoutCreationException PayoutCreationException class 91 | */ 92 | public static function createPayout(Payout $payout): Payout 93 | { 94 | $thisInstance = new self(); 95 | 96 | try { 97 | if (empty($payout->getNotificationURL()) && 98 | in_array(WebhookAutoPopulate::For_Payouts, $thisInstance->config['auto_populate_webhook'])) { 99 | $payout->setNotificationURL(route('laravel-bitpay.webhook.capture')); 100 | } 101 | } catch (\Throwable $exception) { 102 | // Misconfiguration or route macro not in use 103 | } 104 | 105 | return $thisInstance->client->submitPayout($payout); 106 | } 107 | 108 | /** 109 | * Retrieve a payout 110 | * 111 | * @param string $payoutId The id of the payout to retrieve. 112 | * 113 | * @return Payout A BitPay Payout object. 114 | * @throws PayoutQueryException PayoutQueryException class 115 | */ 116 | public static function getPayout(string $payoutId): Payout 117 | { 118 | return (new self)->client->getPayout($payoutId); 119 | } 120 | 121 | /** 122 | * Retrieve payouts based on status 123 | * 124 | * @param string|null $startDate The start date to filter the Payouts. 125 | * @param string|null $endDate The end date to filter the Payout. 126 | * @param string|null $status The payout status you want to query on 127 | * @param string|null $reference The optional reference specified at payout request creation. 128 | * @param int|null $limit Maximum results that the query will return (useful for paging results). 129 | * @param int|null $offset number of results to offset (ex. skip 10 will give you results starting 130 | * with the 11th result). 131 | * 132 | * @return Payout[] A list of BitPay Payout objects. 133 | * @throws PayoutQueryException PayoutQueryException class 134 | */ 135 | public static function getPayouts( 136 | string $startDate = null, 137 | string $endDate = null, 138 | string $status = null, 139 | string $reference = null, 140 | int $limit = null, 141 | int $offset = null): array 142 | { 143 | return (new self())->client->getPayouts( 144 | $startDate, 145 | $endDate, 146 | $status, 147 | $reference, 148 | $limit, 149 | $offset 150 | ); 151 | } 152 | 153 | /** 154 | * Cancel a payout 155 | * 156 | * @param string $payoutId The id of the payout to cancel. 157 | * 158 | * @return bool True if payout was canceled successfully, false otherwise. 159 | * @throws PayoutCancellationException PayoutCancellationException class 160 | */ 161 | public static function cancelPayout(string $payoutId): bool 162 | { 163 | return (new self())->client->cancelPayout($payoutId); 164 | } 165 | 166 | /** 167 | * Request a Payout webhook to be resent. 168 | * 169 | * @param $payoutId string The id of the payout for which you want the last webhook to be resent. 170 | * 171 | * @return bool True if the webhook has been resent for the current payout status, false otherwise. 172 | * @throws PayoutNotificationException PayoutNotificationException class 173 | */ 174 | public static function requestPayoutWebhook(string $payoutId): bool 175 | { 176 | return (new self())->client->requestPayoutNotification($payoutId); 177 | } 178 | 179 | /** 180 | * Create a payout batch 181 | * 182 | * @link https://bitpay.com/api/#rest-api-resources-payouts-create-a-payout-batch 183 | * 184 | * @param PayoutBatch $payoutBatch A PayoutBatch object with request parameters defined. 185 | * 186 | * @return PayoutBatch A BitPay generated PayoutBatch object. 187 | * @throws PayoutBatchCreationException PayoutBatchCreationException class 188 | */ 189 | public static function createPayoutBatch(PayoutBatch $payoutBatch): PayoutBatch 190 | { 191 | $thisInstance = new self(); 192 | 193 | try { 194 | if (empty($payoutBatch->getNotificationURL()) && 195 | in_array(WebhookAutoPopulate::For_Payouts, $thisInstance->config['auto_populate_webhook'])) { 196 | $payoutBatch->setNotificationURL(route('laravel-bitpay.webhook.capture')); 197 | } 198 | } catch (\Throwable $exception) { 199 | // Misconfiguration or route macro not in use 200 | } 201 | 202 | return $thisInstance->client->submitPayoutBatch($payoutBatch); 203 | } 204 | 205 | /** 206 | * Retrieve a payout batch 207 | * 208 | * @link https://bitpay.com/api/#rest-api-resources-payouts-retrieve-a-payout-batch 209 | * 210 | * @param string $payoutBatchId The id of the payout batch to retrieve. 211 | * 212 | * @return PayoutBatch A BitPay PayoutBatch object. 213 | * @throws PayoutBatchQueryException PayoutBatchQueryException class 214 | */ 215 | public static function getPayoutBatch(string $payoutBatchId): PayoutBatch 216 | { 217 | return (new self)->client->getPayoutBatch($payoutBatchId); 218 | } 219 | 220 | /** 221 | * Retrieve payout batches based on status 222 | * 223 | * @link https://bitpay.com/api/#rest-api-resources-payouts-retrieve-payout-batches-based-on-status 224 | * 225 | * @param string|null $startDate The start date to filter the PayoutBatch Batches. 226 | * @param string|null $endDate The end date to filter the PayoutBatch Batches. 227 | * @param string|null $status The payout status you want to query on 228 | * @param int|null $limit Maximum results that the query will return (useful for paging results). 229 | * @param int|null $offset number of results to offset (ex. skip 10 will give you results starting 230 | * with the 11th result). 231 | * 232 | * @return PayoutBatch[] A list of BitPay PayoutBatch objects. 233 | * @throws PayoutBatchQueryException PayoutBatchQueryException class 234 | */ 235 | public static function getPayoutBatches( 236 | string $startDate = null, 237 | string $endDate = null, 238 | string $status = null, 239 | int $limit = null, 240 | int $offset = null): array 241 | { 242 | return (new self())->client->getPayoutBatches( 243 | $startDate, 244 | $endDate, 245 | $status, 246 | $limit, 247 | $offset 248 | ); 249 | } 250 | 251 | /** 252 | * Cancel a payout batch 253 | * 254 | * @link https://bitpay.com/api/#rest-api-resources-payouts-cancel-a-payout-batch 255 | * 256 | * @param string $payoutBatchId The id of the payout batch to cancel. 257 | * 258 | * @return bool True if payout batch was canceled successfully, false otherwise. 259 | * @throws PayoutBatchCancellationException PayoutBatchCancellationException class 260 | */ 261 | public static function cancelPayoutBatch(string $payoutBatchId): bool 262 | { 263 | return (new self())->client->cancelPayoutBatch($payoutBatchId); 264 | } 265 | 266 | /** 267 | * Request a PayoutBatch webhook to be resent. 268 | * 269 | * @param $payoutBatchId string The id of the payout batch for which you want the last webhook to be resent. 270 | * 271 | * @return bool True if the webhook has been resent for the current payout batch status, false otherwise. 272 | * @throws PayoutBatchNotificationException PayoutBatchNotificationException class 273 | */ 274 | public static function requestPayoutBatchWebhook(string $payoutBatchId): bool 275 | { 276 | return (new self())->client->requestPayoutBatchNotification($payoutBatchId); 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LaravelBitPay 2 | 3 | ![LaravelBitPay Social Image](https://banners.beyondco.de/LaravelBitPay.png?theme=light&packageManager=composer+require&packageName=vrajroham%2Flaravel-bitpay&pattern=circuitBoard&style=style_1&description=Transact+in+Bitcoin%2C+Bitcoin+Cash+and+10%2B+other+BitPay-supported+cryptocurrencies+within+your+Laravel+application.&md=1&showWatermark=0&fontSize=100px&images=https%3A%2F%2Flaravel.com%2Fimg%2Flogomark.min.svg) 4 | 5 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/vrajroham/laravel-bitpay.svg?style=for-the-badge)](https://packagist.org/packages/vrajroham/laravel-bitpay) 6 | [![Build Status](https://img.shields.io/travis/vrajroham/laravel-bitpay/master.svg?style=for-the-badge)](https://travis-ci.org/vrajroham/laravel-bitpay) 7 | [![Quality Score](https://img.shields.io/scrutinizer/g/vrajroham/laravel-bitpay.svg?style=for-the-badge)](https://scrutinizer-ci.com/g/vrajroham/laravel-bitpay) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/vrajroham/laravel-bitpay.svg?style=for-the-badge)](https://packagist.org/packages/vrajroham/laravel-bitpay) 9 | 10 | LaravelBitPay enables you and your business to transact in Bitcoin, Bitcoin Cash and 10+ other BitPay-supported 11 | cryptocurrencies within your Laravel application. 12 | 13 | > Requires PHP 7.4+ 14 | 15 | ## :warning: Migration From v4 :warning: 16 | 17 | If upgrading from v4, please follow [MIGRATION.md](./MIGRATION.md) 18 | 19 | ## Supported Resources 20 | 21 | - :white_check_mark: [Invoices](https://bitpay.com/api/#rest-api-resources-invoices) 22 | - :white_check_mark: [Refunds](https://bitpay.com/api/#rest-api-resources-refunds) 23 | - :white_check_mark: [Bills](https://bitpay.com/api/#rest-api-resources-bills) 24 | - :white_check_mark: [Subscriptions](https://bitpay.com/api/#rest-api-resources-subscriptions) 25 | - :white_check_mark: [Settlements](https://bitpay.com/api/#rest-api-resources-settlements) 26 | - :white_check_mark: [Ledgers](https://bitpay.com/api/#rest-api-resources-ledgers) 27 | - :white_check_mark: [Recipients](https://bitpay.com/api/#rest-api-resources-recipients) 28 | - :white_check_mark: [Payouts](https://bitpay.com/api/#rest-api-resources-payouts) 29 | - :white_check_mark: [Currencies](https://bitpay.com/api/#rest-api-resources-currencies) 30 | - :white_check_mark: [Rates](https://bitpay.com/api/#rest-api-resources-rates) 31 | 32 | ## Contents 33 | 34 | - [Installation](#installation) 35 | + [Install Package](#install-package) 36 | + [Publish config file](#publish-config-file) 37 | + [Add configuration values](#add-configuration-values) 38 | + [Generate Key-Pair and API Token(s)](#generate-key-pair-and-api-tokens) 39 | + [Configure Webhooks (Optional)](#configure-webhooks-optional) 40 | + [1. Setup your webhook route](#1-setup-your-webhook-route) 41 | + [2. Setup your webhook listener](#2-setup-your-webhook-listener) 42 | - [Examples](#examples) 43 | + [Invoices](#invoices) 44 | + [Create an invoice](#create-an-invoice) 45 | + [Retrieve an existing invoice](#retrieve-an-existing-invoice) 46 | + [Retrieve a list of existing invoices](#retrieve-a-list-of-existing-invoices) 47 | + [Refunds](#refunds) 48 | + [Refund an invoice](#refund-an-invoice) 49 | + [Retrieve a refund request](#retrieve-a-refund-request) 50 | + [Retrieve all refund requests on an invoice](#retrieve-all-refund-requests-on-an-invoice) 51 | + [Cancel a refund request](#cancel-a-refund-request) 52 | + [Bills](#bills) 53 | + [Create a bill](#create-a-bill) 54 | + [Retrieve a bill](#retrieve-a-bill) 55 | + [Retrieve a list of existing bills](#retrieve-a-list-of-existing-bills) 56 | + [Update a bill](#update-a-bill) 57 | + [Deliver a bill via email](#deliver-a-bill-via-email) 58 | + [Subscriptions](#subscriptions) 59 | + [Create a subscription](#create-a-subscription) 60 | + [Retrieve a subscription](#retrieve-a-subscription) 61 | + [Retrieve a list of existing subscriptions](#retrieve-a-list-of-existing-subscriptions) 62 | + [Update a subscription](#update-a-subscription) 63 | + [Settlements](#settlements) 64 | + [Retrieve settlements](#retrieve-settlements) 65 | + [Retrieve a settlement](#retrieve-a-settlement) 66 | + [Fetch a reconciliation report](#fetch-a-reconciliation-report) 67 | + [Ledgers](#ledgers) 68 | + [Retrieve account balances](#retrieve-account-balances) 69 | + [Retrieve ledger entries](#retrieve-ledger-entries) 70 | + [Recipients](#recipients) 71 | + [Invite Recipients](#invite-recipients) 72 | + [Retrieve a recipient](#retrieve-a-recipient) 73 | + [Retrieve recipients by status](#retrieve-recipients-by-status) 74 | + [Update a recipient](#update-a-recipient) 75 | + [Remove a recipient](#remove-a-recipient) 76 | + [Request a recipient webhook to be resent](#request-a-recipient-webhook-to-be-resent) 77 | + [Payouts](#payouts) 78 | + [Create a payout](#create-a-payout) 79 | + [Create a payout batch](#create-a-payout-batch) 80 | + [Retrieve a payout](#retrieve-a-payout) 81 | + [Retrieve a payout batch](#retrieve-a-payout-batch) 82 | + [Retrieve payouts based on status](#retrieve-payouts-based-on-status) 83 | + [Retrieve payout batches based on status](#retrieve-payout-batches-based-on-status) 84 | + [Cancel a payout](#cancel-a-payout) 85 | + [Cancel a payout batch](#cancel-a-payout-batch) 86 | + [Request a payout webhook to be resent](#request-a-payout-webhook-to-be-resent) 87 | + [Request a payout batch webhook to be resent](#request-a-payout-batch-webhook-to-be-resent) 88 | + [Currencies](#currencies) 89 | + [Retrieve the supported currencies](#retrieve-the-supported-currencies) 90 | + [Rates](#rates) 91 | + [Retrieve the exchange rate table maintained by BitPay](#retrieve-the-exchange-rate-table-maintained-by-bitpay) 92 | + [Retrieve all the rates for a given cryptocurrency](#retrieve-all-the-rates-for-a-given-cryptocurrency) 93 | + [Retrieve the rate for a cryptocurrency / fiat pair](#retrieve-the-rate-for-a-cryptocurrency--fiat-pair) 94 | - [Testing](#testing) 95 | - [Changelog](#changelog) 96 | - [Contributing](#contributing) 97 | - [Security](#security) 98 | - [Credits](#credits) 99 | - [License](#license) 100 | 101 | ## Installation 102 | 103 | ### Install package 104 | 105 | You can install the package via composer: 106 | 107 | ```bash 108 | composer require vrajroham/laravel-bitpay 109 | ``` 110 | 111 | ### Publish config file 112 | 113 | Publish config file with: 114 | 115 | ```bash 116 | php artisan vendor:publish --provider="Vrajroham\LaravelBitpay\LaravelBitpayServiceProvider" 117 | ``` 118 | 119 | This will create a `laravel-bitpay.php` file inside your **config** directory. 120 | 121 | ### Add configuration values 122 | 123 | Add the following keys to your `.env` file and update the values to match your 124 | preferences ([read more about configuration](https://support.bitpay.com/hc/en-us/articles/115003001063-How-do-I-configure-the-PHP-BitPay-Client-Library-)): 125 | 126 | ```dotenv 127 | BITPAY_PRIVATE_KEY_PATH= 128 | BITPAY_NETWORK=testnet 129 | BITPAY_KEY_STORAGE_PASSWORD=RandomPasswordForEncryption 130 | BITPAY_ENABLE_MERCHANT=true 131 | BITPAY_ENABLE_PAYOUT=false 132 | BITPAY_MERCHANT_TOKEN= 133 | BITPAY_PAYOUT_TOKEN= 134 | ``` 135 | 136 | ### Generate Key-Pair and API Token(s) 137 | 138 | The `laravel-bitpay:createkeypair` command generates a BitPay API Token and Pairing Code for each enabled facade: 139 | 140 | ```bash 141 | php artisan laravel-bitpay:createkeypair 142 | ``` 143 | 144 | <center><img src="https://i.ibb.co/JvP3bQb/create-key-pair-command.png" title="Create Key-Pair Command" alt="Create Key-Pair Command"/></center> 145 | 146 | > :information_source: By default, the command will use the (valid) existing private key located 147 | > at `BITPAY_PRIVATE_KEY_PATH`. 148 | > You may specify the `--fresh` or `-f` option to explicitly generate a fresh private key, from which tokens are 149 | > derived. 150 | 151 | After successful API Token generation, you will need to approve it by visiting the provided link. 152 | 153 | :warning: **Note that the `payout` facade must be enabled on your BitPay merchant account before you can approve and use 154 | the related API Token. This means you won't be able to perform actions on the [Recipients](#recipients) 155 | and [Payouts](#payouts) resources. To enable Payouts 156 | functionality, [Contact BitPay Support](https://bitpay.com/request-help/wizard?category=merchant).** 157 | 158 | ### Configure Webhooks (Optional) 159 | 160 | BitPay resource status updates are completely based on webhooks (IPNs). LaravelBitPay is fully capable of automatically 161 | handling webhook requests. Whenever a webhook is received from BitPay's server, `BitpayWebhookReceived` event is 162 | dispatched. Take the following steps to configure your application for webhook listening: 163 | 164 | #### 1. Setup your webhook route 165 | 166 | Resolve the `bitPayWebhook` route macro in your desired route file (`web.php` is recommended). The macro accepts a 167 | single, optional argument, which is the URI path at which you want to receive BitPay webhook `POST` requests. If none is 168 | provided, it defaults to `'laravel-bitpay/webhook'`: 169 | 170 | ```php 171 | // ... your other 'web' routes 172 | 173 | Route::bitPayWebhook(); // https://example.com/laravel-bitpay/webhook 174 | 175 | // OR ... 176 | 177 | Route::bitPayWebhook('receive/webhooks/here'); // https://example.com/receive/webhooks/here 178 | ``` 179 | 180 | > :information_source: To retrieve your newly created webhook route anywhere in your application, 181 | > use: `route('laravel-bitpay.webhook.capture')` 182 | 183 | LaravelBitPay also offers the convenience of auto-populating your configured webhook url on applicable resources. 184 | Specifically when: 185 | 186 | - [Creating an Invoice](#create-an-invoice) 187 | - [Inviting Recipients](#invite-recipients) 188 | - Creating a [Payout](#create-a-payout)/[Payout Batch](#create-a-payout-batch) 189 | 190 | You may enable this feature per-resource by uncommenting the respective entry within the `auto_populate_webhook` array 191 | found in the `laravel-bitpay.php` config file. 192 | 193 | :warning: **If a value is manually set, most likely via `$resource->setNotificationURL('https://...')` during resource 194 | initialization, auto-population is overridden.** 195 | 196 | #### 2. Setup your webhook listener 197 | 198 | Start by generating an event listener: 199 | 200 | ```bash 201 | php artisan make:listener BitPayWebhookListener --event=\Vrajroham\LaravelBitpay\Events\BitpayWebhookReceived 202 | ``` 203 | 204 | Then, implement your application-specific logic in the `handle(...)` function of the generated listener. 205 | 206 | In the following example, we assume you have previously [created an invoice](#create-an-invoice), storing its `token` 207 | on your internal `Order` model: 208 | 209 | ```php 210 | /** 211 | * Handle the webhook event, keeping in mind that the server doesn't trust the client (us), so neither should 212 | * we trust the server. Well, trust, but verify. 213 | * 214 | * @param BitpayWebhookReceived $event 215 | * @return void 216 | */ 217 | public function handle(BitpayWebhookReceived $event) 218 | { 219 | // Extract event payload 220 | $payload = $event->payload; 221 | 222 | // Verify that webhook is for a BitPay Invoice resource 223 | if (in_array($payload['event']['code'], array_keys(BitPayConstants::INVOICE_WEBHOOK_CODES))) { 224 | try { 225 | // Do not trust the webhook data. Pull the referenced Invoice from BitPay's server 226 | $invoice = LaravelBitpay::getInvoice($payload['data']['id']); 227 | 228 | // Now grab our internal Order instance for this supposed Invoice 229 | $order = Order::whereOrderId($invoice->getOrderId())->first(); 230 | 231 | // Verify Invoice token, previously stored at time of creation 232 | // Learn more at: https://github.com/vrajroham/laravel-bitpay#create-an-invoice 233 | if ($invoice->getToken() !== $order->invoice_token) { 234 | return; 235 | } 236 | 237 | $invoice_status = $invoice->getStatus(); 238 | 239 | // Do something about the new Invoice status 240 | if ($invoice_status === InvoiceStatus::Paid) { 241 | $order->update(['status' => $invoice_status]) && OrderStatusChanged::dispatch($order->refresh()); 242 | } 243 | } catch (BitPayException $e) { 244 | Log::error($e); 245 | } 246 | } 247 | } 248 | ``` 249 | 250 | Finally, map your listener to the `BitpayWebhookReceived` event inside the `$listen` array of 251 | your `EventServiceProvider`: 252 | 253 | ```php 254 | /** 255 | * The event listener mappings for the application. 256 | * 257 | * @var array 258 | */ 259 | protected $listen = [ 260 | // ... other event-listener mappings 261 | BitpayWebhookReceived::class => [ 262 | BitPayWebhookListener::class, 263 | ], 264 | ] 265 | ``` 266 | 267 | ## Examples 268 | 269 | ### Invoices 270 | 271 | Invoices are time-sensitive payment requests addressed to specific buyers. An invoice has a fixed price, typically 272 | denominated in fiat currency. It also has an equivalent price in the supported cryptocurrencies, calculated by BitPay, 273 | at a locked exchange rate with an expiration time of 15 minutes. 274 | 275 | #### Create an invoice 276 | 277 | In this example we assume you've already created an instance of your equivalent `Order` model, to be associated with 278 | this Invoice (referred to as `$order`): 279 | 280 | ```php 281 | // Create instance of Invoice 282 | $invoice = LaravelBitpay::Invoice(449.99, Currency::USD); // Always use the BitPay Currency model to prevent typos 283 | 284 | // Set item details (Only 1 item per Invoice) 285 | $invoice->setItemDesc('You "Joe Goldberg" Life-Size Wax Figure'); 286 | $invoice->setItemCode('sku-1234'); 287 | $invoice->setPhysical(true); // Set to false for digital/virtual items 288 | 289 | // Ensure you provide a unique OrderId for each Invoice 290 | $invoice->setOrderId($order->order_id); 291 | 292 | // Create Buyer Instance 293 | $buyer = LaravelBitpay::Buyer(); 294 | $buyer->setName('John Doe'); 295 | $buyer->setEmail('john.doe@example.com'); 296 | $buyer->setAddress1('2630 Hegal Place'); 297 | $buyer->setAddress2('Apt 42'); 298 | $buyer->setLocality('Alexandria'); 299 | $buyer->setRegion('VA'); 300 | $buyer->setPostalCode(23242); 301 | $buyer->setCountry('US'); 302 | $buyer->setNotify(true); // Instructs BitPay to email Buyer about their Invoice 303 | 304 | // Attach Buyer to Invoice 305 | $invoice->setBuyer($buyer); 306 | 307 | // Set URL that Buyer will be redirected to after completing the payment, via GET Request 308 | $invoice->setRedirectURL(route('your-bitpay-success-url')); 309 | // Set URL that Buyer will be redirected to after closing the invoice or after the invoice expires, via GET Request 310 | $invoice->setCloseURL(route('your-bitpay-cancel-url')); 311 | $invoice->setAutoRedirect(true); 312 | 313 | // Optional. Learn more at: https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route 314 | $invoice->setNotificationUrl('https://example.com/your-custom-webhook-url'); 315 | 316 | // This is the recommended IPN format that BitPay advises for all new implementations 317 | $invoice->setExtendedNotifications(true); 318 | 319 | // Create invoice on BitPay's server 320 | $invoice = LaravelBitpay::createInvoice($invoice); 321 | 322 | $invoiceId = $invoice->getId(); 323 | $invoiceToken = $invoice->getToken(); 324 | 325 | // You should save Invoice ID and Token, for your reference 326 | $order->update(['invoice_id' => $invoiceId, 'invoice_token' => $invoiceToken]); 327 | 328 | // Redirect user to the Invoice's hosted URL to complete payment 329 | $paymentUrl = $invoice->getUrl(); 330 | return Redirect::to($paymentUrl); 331 | ``` 332 | 333 | > :information_source: It is highly recommended you store the Invoice ID and Token on your internal model(s). The token 334 | > can come in handy when verifying webhooks. 335 | 336 | #### Retrieve an existing invoice 337 | 338 | ```php 339 | $invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); 340 | ``` 341 | 342 | #### Retrieve a list of existing invoices 343 | 344 | In this example we retrieve all MTD (Month-To-Date) invoices: 345 | 346 | ```php 347 | $startDate = date('Y-m-d', strtotime('first day of this month')); 348 | $endDate = date('Y-m-d'); 349 | 350 | $invoices = LaravelBitpay::getInvoices($startDate, $endDate); 351 | ``` 352 | 353 | #### Request an Invoice webhook to be resent 354 | 355 | ```php 356 | // True if the webhook has been resent for the current invoice status, false otherwise. 357 | $webhookResent = LaravelBitpay::requestInvoiceWebhook('invoiceId_sGsdVsgheF'); 358 | ``` 359 | 360 | ### Refunds 361 | 362 | Refund requests are full or partial refunds associated to an invoice. Fully paid invoices can be refunded via the 363 | merchant's authorization to issue a refund, while underpaid and overpaid invoices are automatically executed by BitPay 364 | to issue the underpayment or overpayment amount to the customer. 365 | 366 | #### Refund an invoice 367 | 368 | The item Jane purchased was dead on arrival. Give back the lady her crypto: 369 | 370 | ```php 371 | $invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); 372 | 373 | $refundRequested = LaravelBitpay::createRefund($invoice, 'jane.doe@example.com', 0.016, 'ETH'); 374 | 375 | if ($refundRequested) { 376 | // Don't just sit there. Do something! 377 | } 378 | ``` 379 | 380 | #### Retrieve a refund request 381 | 382 | Let's periodically retrieve (and check the status of) Jane's refund request: 383 | 384 | ```php 385 | $invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); 386 | 387 | $refund = LaravelBitpay::getRefund($invoice, 'refundId_pUdhjwGjsg'); 388 | ``` 389 | 390 | #### Retrieve all refund requests on an invoice 391 | 392 | In this example we retrieve all refund requests related to Jane's invoice: 393 | 394 | ```php 395 | $invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); 396 | 397 | $refundRequests = LaravelBitpay::getRefunds($invoice); 398 | ``` 399 | 400 | #### Cancel a refund request 401 | 402 | Turns out Jane didn't initially follow the instruction manual. The item works and she no longer wants a refund: 403 | 404 | ```php 405 | $invoice = LaravelBitpay::getInvoice('invoiceId_sGsdVsgheF'); 406 | 407 | $refundRequestCancelled = LaravelBitpay::cancelRefund($invoice->getId(), 'refundId_pUdhjwGjsg'); 408 | ``` 409 | 410 | ### Bills 411 | 412 | Bills are payment requests addressed to specific buyers. Bill line items have fixed prices, typically denominated in 413 | fiat currency. 414 | 415 | #### Create a bill 416 | 417 | In the following example, we create a bill that's due in 10 days: 418 | 419 | ```php 420 | // Initialize Bill 421 | $billData = LaravelBitpay::Bill(); 422 | $billData->setNumber('bill1234-EFGH'); 423 | $billData->setCurrency(Currency::USD); // Always use the BitPay Currency model to prevent typos 424 | $dueDate = date(BitPayConstants::DATETIME_FORMAT, strtotime('+10 days')); // ISO-8601 formatted date 425 | $billData->setDueDate($dueDate); 426 | $billData->setPassProcessingFee(true); // Let the recipient shoulder BitPay's processing fee 427 | 428 | // Prepare Bill recipient's data 429 | $billData->setName('John Doe'); 430 | $billData->setAddress1('2630 Hegal Place'); 431 | $billData->setAddress2('Apt 42'); 432 | $billData->setCity('Alexandria'); 433 | $billData->setState('VA'); 434 | $billData->setZip(23242); 435 | $billData->setCountry('US'); 436 | $billData->setEmail('john.doe@example.com'); 437 | $billData->setCc(['jane.doe@example.com']); 438 | $billData->setPhone('555-123-456'); 439 | 440 | // Prepare Bill's line item(s) 441 | $itemUno = LaravelBitpay::BillItem(); 442 | $itemUno->setDescription('Squid Game "Front Man" Costume'); 443 | $itemUno->setPrice(49.99); 444 | $itemUno->setQuantity(2); 445 | 446 | $itemDos = LaravelBitpay::BillItem(); 447 | $itemDos->setDescription('GOT "House Stark" Sterling Silver Pendant'); 448 | $itemDos->setPrice(35); 449 | $itemDos->setQuantity(1); 450 | 451 | $billData->setItems([$itemUno, $itemDos]); 452 | 453 | // Create Bill 454 | $bill = LaravelBitpay::createBill($billData); 455 | 456 | // Store the Bill's BitPay ID and URL for future reference 457 | $billId = $bill->getId(); 458 | $billPaymentUrl = $bill->getUrl(); 459 | 460 | // OR 461 | 462 | // Redirect the recipient to BitPay's hosted Bill payment page 463 | Redirect::to($billPaymentUrl); 464 | ``` 465 | 466 | #### Retrieve a bill 467 | 468 | ```php 469 | $bill = LaravelBitpay::getBill('bill1234-EFGH'); 470 | ``` 471 | 472 | #### Retrieve a list of existing bills 473 | 474 | You can narrow down the retrieved list by specifying a Bill status: 475 | 476 | ```php 477 | $paidBills = LaravelBitpay::getBills(BillStatus::Paid); 478 | ``` 479 | 480 | #### Update a bill 481 | 482 | We managed to upsell a product to our client. Let's add an extra line item to their existing Bill: 483 | 484 | ```php 485 | $existingBill = LaravelBitpay::getBill('bill1234-EFGH'); 486 | $existingItems = $existingBill->getItems(); 487 | 488 | $billData = LaravelBitpay::Bill(); 489 | $billData->setId($existingBill->getId()); 490 | 491 | $itemTres = LaravelBitpay::BillItem(); 492 | $itemTres->setDescription('The Tomorrow War "White Spike" Life-Size Wax Figure'); 493 | $itemTres->setPrice(189.99); 494 | $itemTres->setQuantity(1); 495 | 496 | $billData->setItems(array_merge($existingItems, [$itemTres])); 497 | 498 | // Update Bill 499 | $updatedBill = LaravelBitpay::updateBill($billData, $billData->getId()); 500 | ``` 501 | 502 | #### Deliver a bill via email 503 | 504 | ```php 505 | $bill = LaravelBitpay::getBill('bill1234-EFGH'); 506 | 507 | $billDelivered = LaravelBitpay::deliverBill($bill->getId(), $bill->getToken()); 508 | 509 | if ($billDelivered) { 510 | // Bill delivered successfully. Do something about that... or not. 511 | } 512 | ``` 513 | 514 | ### Subscriptions 515 | 516 | Subscriptions are repeat billing agreements with specific buyers. BitPay sends bill emails to buyers identified in 517 | active subscriptions according to the specified schedule. 518 | 519 | #### Create a subscription 520 | 521 | Let's create a subscription that's delivered on the 28th of each month and due on the first of the following month, at 9 522 | AM, respectively: 523 | 524 | ```php 525 | // Initialize Subscription 526 | $subscriptionData = LaravelBitpay::Subscription(); 527 | $subscriptionData->setSchedule(BitPayConstants::SUBSCRIPTION_SCHEDULE_MONTHLY); 528 | 529 | // Optional recurring bill data 530 | $billData = [ 531 | 'number' => 'subscription1234-ABCD', 532 | 'name' => 'John Doe', 533 | 'address1' => '2630 Hegal Place', 534 | 'address2' => 'Apt 42', 535 | 'city' => 'Alexandria', 536 | 'state' => 'VA', 537 | 'zip' => 23242, 538 | 'country' => 'US', 539 | 'cc' => ['jane.doe@example.com'], 540 | 'phone' => '555-123-456', 541 | 'passProcessingFee' => true, 542 | ]; 543 | 544 | $dueDate = date(BitPayConstants::DATETIME_FORMAT, strtotime('first day of next month 9 AM')); 545 | 546 | $billItems = array( 547 | LaravelBitpay::SubscriptionItem(100.00, 1, 'Web Hosting - 4 CPUs | 16GB Memory | 400GB SSD'), 548 | LaravelBitpay::SubscriptionItem(80.00, 1, 'Basic Website Maintenance'), 549 | ); 550 | 551 | // Autofill optional bill data 552 | $mapper = new JsonMapper(); 553 | $billData = $mapper->map( 554 | $billData, 555 | LaravelBitpay::BillData( 556 | Currency::USD, // Always use the BitPay Currency model to prevent typos 557 | 'john.doe@example.com', 558 | $dueDate, 559 | $billItems 560 | ) 561 | ); 562 | 563 | $subscriptionData->setBillData($billData); 564 | 565 | // A little wizardry to always get the 28th day of the current month (leap year safe) 566 | $deliveryDate = strtotime('first day of this month 9 AM'); 567 | $deliveryDate = new \DateTime("@$deliveryDate"); 568 | $deliveryDate = $deliveryDate->modify('+27 days')->getTimestamp(); 569 | $deliveryDate = date(BitPayConstants::DATETIME_FORMAT, $deliveryDate); 570 | 571 | $subscriptionData->setNextDelivery($deliveryDate); 572 | 573 | // Create the Subscription on BitPay 574 | $subscription = LaravelBitpay::createSubscription($subscriptionData); 575 | 576 | // You may then store the Subscription ID for future reference 577 | $subscriptionId = $subscription->getId(); 578 | ``` 579 | 580 | #### Retrieve a subscription 581 | 582 | ```php 583 | $subscription = LaravelBitpay::getSubscription('6gqe8y5mkc5Qx2a9zmspgx'); 584 | ``` 585 | 586 | #### Retrieve a list of existing subscriptions 587 | 588 | You can narrow down the retrieved list by specifying a Subscription status: 589 | 590 | ```php 591 | $activeSubscriptions = LaravelBitpay::getSubscriptions(SubscriptionStatus::Active); 592 | ``` 593 | 594 | #### Update a subscription 595 | 596 | In this example we activate a Subscription by updating its status: 597 | 598 | ```php 599 | $subscriptionData = LaravelBitpay::Subscription(); 600 | $subscriptionData->setId('6gqe8y5mkc5Qx2a9zmspgx'); 601 | $subscriptionData->setStatus(SubscriptionStatus::Active); 602 | 603 | $activatedSubscription = LaravelBitpay::updateSubscription($subscriptionData, $subscriptionData->getId()); 604 | ``` 605 | 606 | ### Settlements 607 | 608 | Settlements are transfers of payment profits from BitPay to bank accounts and cryptocurrency wallets owned by merchants, 609 | partners, etc. 610 | 611 | #### Retrieve settlements 612 | 613 | In this example we retrieve completed YTD (Year-To-Date) settlements denominated in Euros (EUR). We only need 100 614 | records, starting from the 5th one: 615 | 616 | ```php 617 | $startDate = date('Y-m-d', strtotime('first day of this year')); 618 | $endDate = date('Y-m-d'); 619 | 620 | $eurSettlements = LaravelBitpay::getSettlements( 621 | Currency::EUR, 622 | $startDate, 623 | $endDate, 624 | BitPayConstants::SETTLEMENT_STATUS_COMPLETED, 625 | 100, 626 | 4 627 | ); 628 | ``` 629 | 630 | #### Retrieve a settlement 631 | 632 | ```php 633 | $settlement = LaravelBitpay::getSettlement('settlementId_uidwb3668'); 634 | ``` 635 | 636 | #### Fetch a reconciliation report 637 | 638 | A reconciliation report is a detailed report of the activity within the settlement period, in order to reconcile 639 | incoming settlements from BitPay. 640 | 641 | ```php 642 | $settlement = LaravelBitpay::getSettlement('settlementId_uidwb3668'); 643 | 644 | $settlementReport = LaravelBitpay::getSettlementReconciliationReport($settlement); 645 | ``` 646 | 647 | ### Ledgers 648 | 649 | Ledgers are records of money movement. 650 | 651 | #### Retrieve account balances 652 | 653 | ```php 654 | $accountBalances = LaravelBitpay::getLedgers(); 655 | ``` 656 | 657 | #### Retrieve ledger entries 658 | 659 | In this example we retrieve MTD (Month-To-Date) ledger entries denominated in United States Dollars (USD). 660 | 661 | ```php 662 | $startDate = date('Y-m-d', strtotime('first day of this month')); 663 | $endDate = date('Y-m-d'); 664 | 665 | $usdLedgerEntries = LaravelBitpay::getLedger(Currency::USD, $startDate, $endDate); 666 | ``` 667 | 668 | ### Recipients 669 | 670 | The Recipient resource allows a merchant to invite their clients to signup for a BitPay personal account. 671 | 672 | :warning: **Your BitPay Merchant account must be authorized for Payouts functionality. To enable Payouts 673 | functionality, [Contact BitPay Support](https://bitpay.com/request-help/wizard?category=merchant).** 674 | 675 | #### Invite Recipients 676 | 677 | ```php 678 | // Init individual recipients 679 | $jane = LaravelBitpay::PayoutRecipient('jane.doe@example.com', 'Plain Jane'); 680 | $ada = LaravelBitpay::PayoutRecipient('ada@cardano.org', 'Ada Lovelace'); 681 | 682 | // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route 683 | $ada->setNotificationUrl('https://example.com/your-custom-webhook-url'); 684 | 685 | // Batch all individual recipients 686 | $recipients = LaravelBitpay::PayoutRecipients([$jane, $ada]); 687 | 688 | // Submit invites 689 | $recipientsInvited = LaravelBitpay::invitePayoutRecipients($recipients); 690 | 691 | // Do something with the returned invitees 692 | foreach ($recipientsInvited as $recipient) { 693 | $recipientId = $recipient->getId(); 694 | $recipientToken = $recipient->getToken(); 695 | 696 | // ... store Recipient ID and Token somewhere persistent 697 | 698 | // Perform other desired actions 699 | \App\Events\LookOutForAnInviteEmail::dispatch($recipient->getEmail()); 700 | } 701 | ``` 702 | 703 | > :information_source: It is highly recommended you store the Recipient ID and Token on your internal model(s). The 704 | > token can come in handy when verifying webhooks. 705 | 706 | #### Retrieve a recipient 707 | 708 | ```php 709 | $recipient = LaravelBitpay::getPayoutRecipient('recipientId_adaLovelace') 710 | ``` 711 | 712 | #### Retrieve recipients by status 713 | 714 | In this example, we retrieve 100 recipients (starting from the 50th) that have passed the good 'ole Onfido ID 715 | verification checks: 716 | 717 | ```php 718 | $verifiedRecipients = LaravelBitpay::getPayoutRecipients(RecipientStatus::VERIFIED, 100, 49); 719 | ``` 720 | 721 | #### Update a recipient 722 | 723 | ```php 724 | $recipient = LaravelBitpay::getPayoutRecipient('recipientId_adaLovelace'); 725 | $recipient->setLabel('Cardano To The Moon'); 726 | 727 | $updatedRecipient = LaravelBitpay::updatePayoutRecipient($recipient->getId(), $recipient); 728 | ``` 729 | 730 | #### Remove a recipient 731 | 732 | ```php 733 | $recipientRemoved = LaravelBitpay::removePayoutRecipient('recipientId_janeDoe'); 734 | ``` 735 | 736 | #### Request a recipient webhook to be resent 737 | 738 | ```php 739 | // True if the webhook has been resent for the current recipient status, false otherwise. 740 | $webhookResent = LaravelBitpay::requestPayoutRecipientWebhook('recipientId_adaLovelace'); 741 | ``` 742 | 743 | ### Payouts 744 | 745 | Payouts are individual (or batches of) bitcoin payments to employees, customers, partners, etc. 746 | 747 | :warning: **Your BitPay Merchant account must be authorized for Payouts functionality. To enable Payouts 748 | functionality, [Contact BitPay Support](https://bitpay.com/request-help/wizard?category=merchant).** 749 | 750 | #### Create a payout 751 | 752 | Let's assume Ada Lovelace accepted our [invitation](#invite-recipients). In this example, we schedule her an individual 753 | payout for a 5-star rating she received from a referral: 754 | 755 | ```php 756 | // Initialize a Payout 757 | // Pay Ada in USD and record it on the BTC ledger 758 | $payoutData = LaravelBitpay::Payout(50.00, Currency::USD, Currency::BTC); 759 | 760 | // Set Payout details 761 | $payoutData->setRecipientId('recipientId_adaLovelace'); // From previously invited Recipient 762 | $payoutData->setReference('1234'); // Uniquely identifies an equivalent payout entry in your system 763 | $payoutData->setLabel('5-Star Bonus Affiliate Payment #1234 for Dec 2021'); 764 | $payoutData->setEffectiveDate('2021-12-31'); 765 | 766 | // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route 767 | $payoutData->setNotificationURL('https://example.com/your-custom-webhook-url'); 768 | 769 | // Create Payout on BitPay's server 770 | $payout = LaravelBitpay::createPayout($payoutData); 771 | 772 | $payoutId = $payout->getId(); 773 | $payoutToken = $payout->getToken(); 774 | 775 | // ... store Payout ID and Token somewhere persistent 776 | ``` 777 | 778 | > :information_source: It is highly recommended you store the Payout ID and Token on your internal model(s). The token 779 | > can come in handy when verifying webhooks. 780 | 781 | #### Create a payout batch 782 | 783 | Let's pay our two top-tier affiliates for all their hard work, batching both payments into a single API call, for the 784 | efficiency of it. 785 | 786 | ```php 787 | // Initialize a Payout Batch 788 | $payoutBatchData = LaravelBitpay::PayoutBatch(Currency::USD); // Pay recipients in USD 789 | $payoutBatchData->setLedgerCurrency(Currency::ETH); // Record the payout batch on the ETH ledger 790 | $payoutBatchData->setAmount(500.00); 791 | $payoutBatchData->setReference('Aff_Jan-Feb_2022'); // Uniquely identifies an equivalent payout batch in your system 792 | $payoutBatchData->setLabel('Affiliate Payments for Jan-Feb 2022'); 793 | $payoutBatchData->setEffectiveDate('2022-02-28'); 794 | 795 | // Optional. Learn more at https://github.com/vrajroham/laravel-bitpay#1-setup-your-webhook-route 796 | $payoutBatchData->setNotificationURL('https://example.com/your-custom-webhook-url'); 797 | 798 | // Define Instruction(s) 799 | $payJane = LaravelBitpay::PayoutInstruction( 800 | 250.00, 801 | RecipientReferenceMethod::RECIPIENT_ID, 802 | 'recipientId_janeDoe' 803 | ); 804 | $payJane->setLabel('Affiliate Payment #1234 for Jan-Feb 2022'); 805 | 806 | $payAda = LaravelBitpay::PayoutInstruction( 807 | 250.00, 808 | RecipientReferenceMethod::RECIPIENT_ID, 809 | 'recipientId_adaLovelace' 810 | ); 811 | $payAda->setLabel('Affiliate Payment #5678 for Jan-Feb 2022'); 812 | 813 | // Attach Instruction(s) to Payout Batch 814 | $payoutBatchData->setInstructions([$payJane, $payAda]); 815 | 816 | // Create Payout Batch on BitPay's server 817 | $payoutBatch = LaravelBitpay::createPayoutBatch($payoutBatchData); 818 | 819 | $payoutBatchId = $payoutBatch->getId(); 820 | $payoutBatchToken = $payoutBatch->getToken(); 821 | 822 | // ... store Payout Batch ID and Token somewhere persistent 823 | ``` 824 | 825 | > :information_source: It is highly recommended you store the Payout Batch ID and Token on your internal model(s). 826 | > The token can come in handy when verifying webhooks. 827 | 828 | #### Retrieve a payout 829 | 830 | ```php 831 | $payout = LaravelBitpay::getPayout('payoutId_jws43dbnfpg'); 832 | ``` 833 | 834 | #### Retrieve a payout batch 835 | 836 | ```php 837 | $payoutBatch = LaravelBitpay::getPayoutBatch('payoutBatchId_jws43dbnfpg'); 838 | ``` 839 | 840 | #### Retrieve payouts based on status 841 | 842 | In this example, we retrieve all completed, Year-To-Date (YTD) payouts. 843 | 844 | ```php 845 | $startDate = date('Y-m-d', strtotime('first day of this year')); 846 | $endDate = date('Y-m-d'); 847 | 848 | $completedPayouts = LaravelBitpay::getPayouts($startDate, $endDate, PayoutStatus::Complete); 849 | ``` 850 | 851 | #### Retrieve payout batches based on status 852 | 853 | In this example, we retrieve all cancelled, Year-To-Date (YTD) payout batches. 854 | 855 | ```php 856 | $startDate = date('Y-m-d', strtotime('first day of this year')); 857 | $endDate = date('Y-m-d'); 858 | 859 | $cancelledPayoutBatches = LaravelBitpay::getPayoutBatches($startDate, $endDate, PayoutStatus::Cancelled); 860 | ``` 861 | 862 | #### Cancel a payout 863 | 864 | ```php 865 | $payoutCancelled = LaravelBitpay::cancelPayout('payoutId_jws43dbnfpg'); 866 | ``` 867 | 868 | #### Cancel a payout batch 869 | 870 | ```php 871 | $payoutBatchCancelled = LaravelBitpay::cancelPayoutBatch('payoutBatchId_jws43dbnfpg'); 872 | ``` 873 | 874 | #### Request a payout webhook to be resent 875 | 876 | ```php 877 | // True if the webhook has been resent for the current payout status, false otherwise. 878 | $webhookResent = LaravelBitpay::requestPayoutWebhook('payoutId_jws43dbnfpg'); 879 | ``` 880 | 881 | #### Request a payout batch webhook to be resent 882 | 883 | ```php 884 | // True if the webhook has been resent for the current payout batch status, false otherwise. 885 | $webhookResent = LaravelBitpay::requestPayoutBatchWebhook('payoutBatchId_jws43dbnfpg'); 886 | ``` 887 | 888 | ### Currencies 889 | 890 | Currencies are fiat currencies supported by BitPay. 891 | 892 | #### Retrieve the supported currencies 893 | 894 | In this example, we retrieve the list of supported BitPay Currency objects. 895 | 896 | ```php 897 | $supportedCurrencies = LaravelBitpay::getCurrencies(); 898 | ``` 899 | 900 | ### Rates 901 | 902 | Rates are exchange rates, representing the number of fiat currency units equivalent to one BTC. 903 | 904 | #### Retrieve the exchange rate table maintained by BitPay 905 | 906 | ```php 907 | $rates = LaravelBitpay::getRates(); 908 | 909 | $btcToUsdRate = $rates->getRate(Currency::USD); // Always use the BitPay Currency model to prevent typos 910 | ``` 911 | 912 | #### Retrieve all the rates for a given cryptocurrency 913 | 914 | ```php 915 | $ethRates = LaravelBitpay::getCurrencyRates(Currency::ETH); 916 | 917 | $ethToUsdRate = $ethRates->getRate(Currency::USD); 918 | ``` 919 | 920 | #### Retrieve the rate for a cryptocurrency / fiat pair 921 | 922 | ```php 923 | $dogeToUsdRate = LaravelBitpay::getCurrencyPairRate(Currrency::DOGE, Currency::USD); 924 | ``` 925 | 926 | ## Testing 927 | 928 | ```bash 929 | composer test 930 | ``` 931 | 932 | ## Changelog 933 | 934 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 935 | 936 | ## Contributing 937 | 938 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 939 | 940 | ## Security 941 | 942 | If you discover any security related issues, please email vaibhavraj@vrajroham.me or iamalexstewart@gmail.com instead of 943 | using the issue tracker. 944 | 945 | ## Credits 946 | 947 | - [Vaibhavraj Roham](https://github.com/vrajroham) 948 | - [Alex Stewart](https://github.com/alexstewartja) 949 | - [All Contributors](../../contributors) 950 | 951 | ## License 952 | 953 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 954 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = true 7 | max_line_length = 120 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = false 13 | ij_smart_tabs = false 14 | ij_visual_guides = none 15 | ij_wrap_on_typing = false 16 | 17 | [*.blade.php] 18 | ij_blade_keep_indents_on_empty_lines = false 19 | 20 | [*.css] 21 | ij_css_align_closing_brace_with_properties = false 22 | ij_css_blank_lines_around_nested_selector = 1 23 | ij_css_blank_lines_between_blocks = 1 24 | ij_css_brace_placement = end_of_line 25 | ij_css_enforce_quotes_on_format = false 26 | ij_css_hex_color_long_format = false 27 | ij_css_hex_color_lower_case = false 28 | ij_css_hex_color_short_format = false 29 | ij_css_hex_color_upper_case = false 30 | ij_css_keep_blank_lines_in_code = 2 31 | ij_css_keep_indents_on_empty_lines = false 32 | ij_css_keep_single_line_blocks = false 33 | ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 34 | ij_css_space_after_colon = true 35 | ij_css_space_before_opening_brace = true 36 | ij_css_use_double_quotes = true 37 | ij_css_value_alignment = do_not_align 38 | 39 | [*.feature] 40 | indent_size = 2 41 | ij_gherkin_keep_indents_on_empty_lines = false 42 | 43 | [*.haml] 44 | indent_size = 2 45 | ij_haml_keep_indents_on_empty_lines = false 46 | 47 | [*.less] 48 | indent_size = 2 49 | ij_less_align_closing_brace_with_properties = false 50 | ij_less_blank_lines_around_nested_selector = 1 51 | ij_less_blank_lines_between_blocks = 1 52 | ij_less_brace_placement = 0 53 | ij_less_enforce_quotes_on_format = false 54 | ij_less_hex_color_long_format = false 55 | ij_less_hex_color_lower_case = false 56 | ij_less_hex_color_short_format = false 57 | ij_less_hex_color_upper_case = false 58 | ij_less_keep_blank_lines_in_code = 2 59 | ij_less_keep_indents_on_empty_lines = false 60 | ij_less_keep_single_line_blocks = false 61 | ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 62 | ij_less_space_after_colon = true 63 | ij_less_space_before_opening_brace = true 64 | ij_less_use_double_quotes = true 65 | ij_less_value_alignment = 0 66 | 67 | [*.sass] 68 | indent_size = 2 69 | ij_sass_align_closing_brace_with_properties = false 70 | ij_sass_blank_lines_around_nested_selector = 1 71 | ij_sass_blank_lines_between_blocks = 1 72 | ij_sass_brace_placement = 0 73 | ij_sass_enforce_quotes_on_format = false 74 | ij_sass_hex_color_long_format = false 75 | ij_sass_hex_color_lower_case = false 76 | ij_sass_hex_color_short_format = false 77 | ij_sass_hex_color_upper_case = false 78 | ij_sass_keep_blank_lines_in_code = 2 79 | ij_sass_keep_indents_on_empty_lines = false 80 | ij_sass_keep_single_line_blocks = false 81 | ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 82 | ij_sass_space_after_colon = true 83 | ij_sass_space_before_opening_brace = true 84 | ij_sass_use_double_quotes = true 85 | ij_sass_value_alignment = 0 86 | 87 | [*.scss] 88 | indent_size = 2 89 | ij_scss_align_closing_brace_with_properties = false 90 | ij_scss_blank_lines_around_nested_selector = 1 91 | ij_scss_blank_lines_between_blocks = 1 92 | ij_scss_brace_placement = 0 93 | ij_scss_enforce_quotes_on_format = false 94 | ij_scss_hex_color_long_format = false 95 | ij_scss_hex_color_lower_case = false 96 | ij_scss_hex_color_short_format = false 97 | ij_scss_hex_color_upper_case = false 98 | ij_scss_keep_blank_lines_in_code = 2 99 | ij_scss_keep_indents_on_empty_lines = false 100 | ij_scss_keep_single_line_blocks = false 101 | ij_scss_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow 102 | ij_scss_space_after_colon = true 103 | ij_scss_space_before_opening_brace = true 104 | ij_scss_use_double_quotes = true 105 | ij_scss_value_alignment = 0 106 | 107 | [*.twig] 108 | ij_twig_keep_indents_on_empty_lines = false 109 | ij_twig_spaces_inside_comments_delimiters = true 110 | ij_twig_spaces_inside_delimiters = true 111 | ij_twig_spaces_inside_variable_delimiters = true 112 | 113 | [*.vue] 114 | indent_size = 2 115 | tab_width = 2 116 | ij_continuation_indent_size = 4 117 | ij_vue_indent_children_of_top_level = template 118 | ij_vue_interpolation_new_line_after_start_delimiter = true 119 | ij_vue_interpolation_new_line_before_end_delimiter = true 120 | ij_vue_interpolation_wrap = off 121 | ij_vue_keep_indents_on_empty_lines = false 122 | ij_vue_spaces_within_interpolation_expressions = true 123 | ij_vue_uniform_indent = true 124 | 125 | [.editorconfig] 126 | ij_editorconfig_align_group_field_declarations = false 127 | ij_editorconfig_space_after_colon = false 128 | ij_editorconfig_space_after_comma = true 129 | ij_editorconfig_space_before_colon = false 130 | ij_editorconfig_space_before_comma = false 131 | ij_editorconfig_spaces_around_assignment_operators = true 132 | 133 | [{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] 134 | ij_xml_align_attributes = true 135 | ij_xml_align_text = false 136 | ij_xml_attribute_wrap = normal 137 | ij_xml_block_comment_at_first_column = true 138 | ij_xml_keep_blank_lines = 2 139 | ij_xml_keep_indents_on_empty_lines = false 140 | ij_xml_keep_line_breaks = true 141 | ij_xml_keep_line_breaks_in_text = true 142 | ij_xml_keep_whitespaces = false 143 | ij_xml_keep_whitespaces_around_cdata = preserve 144 | ij_xml_keep_whitespaces_inside_cdata = false 145 | ij_xml_line_comment_at_first_column = true 146 | ij_xml_space_after_tag_name = false 147 | ij_xml_space_around_equals_in_attribute = false 148 | ij_xml_space_inside_empty_tag = false 149 | ij_xml_text_wrap = normal 150 | 151 | [{*.ats,*.ts}] 152 | ij_continuation_indent_size = 4 153 | ij_typescript_align_imports = false 154 | ij_typescript_align_multiline_array_initializer_expression = false 155 | ij_typescript_align_multiline_binary_operation = false 156 | ij_typescript_align_multiline_chained_methods = false 157 | ij_typescript_align_multiline_extends_list = false 158 | ij_typescript_align_multiline_for = true 159 | ij_typescript_align_multiline_parameters = true 160 | ij_typescript_align_multiline_parameters_in_calls = false 161 | ij_typescript_align_multiline_ternary_operation = false 162 | ij_typescript_align_object_properties = 0 163 | ij_typescript_align_union_types = false 164 | ij_typescript_align_var_statements = 0 165 | ij_typescript_array_initializer_new_line_after_left_brace = false 166 | ij_typescript_array_initializer_right_brace_on_new_line = false 167 | ij_typescript_array_initializer_wrap = off 168 | ij_typescript_assignment_wrap = off 169 | ij_typescript_binary_operation_sign_on_next_line = false 170 | ij_typescript_binary_operation_wrap = off 171 | ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 172 | ij_typescript_blank_lines_after_imports = 1 173 | ij_typescript_blank_lines_around_class = 1 174 | ij_typescript_blank_lines_around_field = 0 175 | ij_typescript_blank_lines_around_field_in_interface = 0 176 | ij_typescript_blank_lines_around_function = 1 177 | ij_typescript_blank_lines_around_method = 1 178 | ij_typescript_blank_lines_around_method_in_interface = 1 179 | ij_typescript_block_brace_style = end_of_line 180 | ij_typescript_call_parameters_new_line_after_left_paren = false 181 | ij_typescript_call_parameters_right_paren_on_new_line = false 182 | ij_typescript_call_parameters_wrap = off 183 | ij_typescript_catch_on_new_line = false 184 | ij_typescript_chained_call_dot_on_new_line = true 185 | ij_typescript_class_brace_style = end_of_line 186 | ij_typescript_comma_on_new_line = false 187 | ij_typescript_do_while_brace_force = never 188 | ij_typescript_else_on_new_line = false 189 | ij_typescript_enforce_trailing_comma = keep 190 | ij_typescript_extends_keyword_wrap = off 191 | ij_typescript_extends_list_wrap = off 192 | ij_typescript_field_prefix = _ 193 | ij_typescript_file_name_style = relaxed 194 | ij_typescript_finally_on_new_line = false 195 | ij_typescript_for_brace_force = never 196 | ij_typescript_for_statement_new_line_after_left_paren = false 197 | ij_typescript_for_statement_right_paren_on_new_line = false 198 | ij_typescript_for_statement_wrap = off 199 | ij_typescript_force_quote_style = false 200 | ij_typescript_force_semicolon_style = false 201 | ij_typescript_function_expression_brace_style = end_of_line 202 | ij_typescript_if_brace_force = never 203 | ij_typescript_import_merge_members = global 204 | ij_typescript_import_prefer_absolute_path = global 205 | ij_typescript_import_sort_members = true 206 | ij_typescript_import_sort_module_name = false 207 | ij_typescript_import_use_node_resolution = true 208 | ij_typescript_imports_wrap = on_every_item 209 | ij_typescript_indent_case_from_switch = true 210 | ij_typescript_indent_chained_calls = true 211 | ij_typescript_indent_package_children = 0 212 | ij_typescript_jsdoc_include_types = false 213 | ij_typescript_jsx_attribute_value = braces 214 | ij_typescript_keep_blank_lines_in_code = 2 215 | ij_typescript_keep_first_column_comment = true 216 | ij_typescript_keep_indents_on_empty_lines = false 217 | ij_typescript_keep_line_breaks = true 218 | ij_typescript_keep_simple_blocks_in_one_line = false 219 | ij_typescript_keep_simple_methods_in_one_line = false 220 | ij_typescript_line_comment_add_space = true 221 | ij_typescript_line_comment_at_first_column = false 222 | ij_typescript_method_brace_style = end_of_line 223 | ij_typescript_method_call_chain_wrap = off 224 | ij_typescript_method_parameters_new_line_after_left_paren = false 225 | ij_typescript_method_parameters_right_paren_on_new_line = false 226 | ij_typescript_method_parameters_wrap = off 227 | ij_typescript_object_literal_wrap = on_every_item 228 | ij_typescript_parentheses_expression_new_line_after_left_paren = false 229 | ij_typescript_parentheses_expression_right_paren_on_new_line = false 230 | ij_typescript_place_assignment_sign_on_next_line = false 231 | ij_typescript_prefer_as_type_cast = false 232 | ij_typescript_prefer_explicit_types_function_expression_returns = false 233 | ij_typescript_prefer_explicit_types_function_returns = false 234 | ij_typescript_prefer_explicit_types_vars_fields = false 235 | ij_typescript_prefer_parameters_wrap = false 236 | ij_typescript_reformat_c_style_comments = false 237 | ij_typescript_space_after_colon = true 238 | ij_typescript_space_after_comma = true 239 | ij_typescript_space_after_dots_in_rest_parameter = false 240 | ij_typescript_space_after_generator_mult = true 241 | ij_typescript_space_after_property_colon = true 242 | ij_typescript_space_after_quest = true 243 | ij_typescript_space_after_type_colon = true 244 | ij_typescript_space_after_unary_not = false 245 | ij_typescript_space_before_async_arrow_lparen = true 246 | ij_typescript_space_before_catch_keyword = true 247 | ij_typescript_space_before_catch_left_brace = true 248 | ij_typescript_space_before_catch_parentheses = true 249 | ij_typescript_space_before_class_lbrace = true 250 | ij_typescript_space_before_class_left_brace = true 251 | ij_typescript_space_before_colon = true 252 | ij_typescript_space_before_comma = false 253 | ij_typescript_space_before_do_left_brace = true 254 | ij_typescript_space_before_else_keyword = true 255 | ij_typescript_space_before_else_left_brace = true 256 | ij_typescript_space_before_finally_keyword = true 257 | ij_typescript_space_before_finally_left_brace = true 258 | ij_typescript_space_before_for_left_brace = true 259 | ij_typescript_space_before_for_parentheses = true 260 | ij_typescript_space_before_for_semicolon = false 261 | ij_typescript_space_before_function_left_parenth = true 262 | ij_typescript_space_before_generator_mult = false 263 | ij_typescript_space_before_if_left_brace = true 264 | ij_typescript_space_before_if_parentheses = true 265 | ij_typescript_space_before_method_call_parentheses = false 266 | ij_typescript_space_before_method_left_brace = true 267 | ij_typescript_space_before_method_parentheses = false 268 | ij_typescript_space_before_property_colon = false 269 | ij_typescript_space_before_quest = true 270 | ij_typescript_space_before_switch_left_brace = true 271 | ij_typescript_space_before_switch_parentheses = true 272 | ij_typescript_space_before_try_left_brace = true 273 | ij_typescript_space_before_type_colon = false 274 | ij_typescript_space_before_unary_not = false 275 | ij_typescript_space_before_while_keyword = true 276 | ij_typescript_space_before_while_left_brace = true 277 | ij_typescript_space_before_while_parentheses = true 278 | ij_typescript_spaces_around_additive_operators = true 279 | ij_typescript_spaces_around_arrow_function_operator = true 280 | ij_typescript_spaces_around_assignment_operators = true 281 | ij_typescript_spaces_around_bitwise_operators = true 282 | ij_typescript_spaces_around_equality_operators = true 283 | ij_typescript_spaces_around_logical_operators = true 284 | ij_typescript_spaces_around_multiplicative_operators = true 285 | ij_typescript_spaces_around_relational_operators = true 286 | ij_typescript_spaces_around_shift_operators = true 287 | ij_typescript_spaces_around_unary_operator = false 288 | ij_typescript_spaces_within_array_initializer_brackets = false 289 | ij_typescript_spaces_within_brackets = false 290 | ij_typescript_spaces_within_catch_parentheses = false 291 | ij_typescript_spaces_within_for_parentheses = false 292 | ij_typescript_spaces_within_if_parentheses = false 293 | ij_typescript_spaces_within_imports = false 294 | ij_typescript_spaces_within_interpolation_expressions = false 295 | ij_typescript_spaces_within_method_call_parentheses = false 296 | ij_typescript_spaces_within_method_parentheses = false 297 | ij_typescript_spaces_within_object_literal_braces = false 298 | ij_typescript_spaces_within_object_type_braces = true 299 | ij_typescript_spaces_within_parentheses = false 300 | ij_typescript_spaces_within_switch_parentheses = false 301 | ij_typescript_spaces_within_type_assertion = false 302 | ij_typescript_spaces_within_union_types = true 303 | ij_typescript_spaces_within_while_parentheses = false 304 | ij_typescript_special_else_if_treatment = true 305 | ij_typescript_ternary_operation_signs_on_next_line = false 306 | ij_typescript_ternary_operation_wrap = off 307 | ij_typescript_union_types_wrap = on_every_item 308 | ij_typescript_use_chained_calls_group_indents = false 309 | ij_typescript_use_double_quotes = true 310 | ij_typescript_use_explicit_js_extension = global 311 | ij_typescript_use_path_mapping = always 312 | ij_typescript_use_public_modifier = false 313 | ij_typescript_use_semicolon_after_statement = true 314 | ij_typescript_var_declaration_wrap = normal 315 | ij_typescript_while_brace_force = never 316 | ij_typescript_while_on_new_line = false 317 | ij_typescript_wrap_comments = false 318 | 319 | [{*.bash,*.sh,*.zsh,release}] 320 | indent_size = 2 321 | tab_width = 2 322 | ij_shell_binary_ops_start_line = false 323 | ij_shell_keep_column_alignment_padding = false 324 | ij_shell_minify_program = false 325 | ij_shell_redirect_followed_by_space = false 326 | ij_shell_switch_cases_indented = false 327 | 328 | [{*.cjs,*.js,ProductSearchAutoComplete,RegisterSessionList}] 329 | indent_size = 2 330 | tab_width = 2 331 | ij_continuation_indent_size = 2 332 | ij_javascript_align_imports = false 333 | ij_javascript_align_multiline_array_initializer_expression = false 334 | ij_javascript_align_multiline_binary_operation = false 335 | ij_javascript_align_multiline_chained_methods = false 336 | ij_javascript_align_multiline_extends_list = false 337 | ij_javascript_align_multiline_for = true 338 | ij_javascript_align_multiline_parameters = false 339 | ij_javascript_align_multiline_parameters_in_calls = false 340 | ij_javascript_align_multiline_ternary_operation = false 341 | ij_javascript_align_object_properties = 0 342 | ij_javascript_align_union_types = false 343 | ij_javascript_align_var_statements = 0 344 | ij_javascript_array_initializer_new_line_after_left_brace = false 345 | ij_javascript_array_initializer_right_brace_on_new_line = false 346 | ij_javascript_array_initializer_wrap = off 347 | ij_javascript_assignment_wrap = off 348 | ij_javascript_binary_operation_sign_on_next_line = false 349 | ij_javascript_binary_operation_wrap = off 350 | ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 351 | ij_javascript_blank_lines_after_imports = 1 352 | ij_javascript_blank_lines_around_class = 1 353 | ij_javascript_blank_lines_around_field = 0 354 | ij_javascript_blank_lines_around_function = 1 355 | ij_javascript_blank_lines_around_method = 1 356 | ij_javascript_block_brace_style = end_of_line 357 | ij_javascript_call_parameters_new_line_after_left_paren = false 358 | ij_javascript_call_parameters_right_paren_on_new_line = false 359 | ij_javascript_call_parameters_wrap = off 360 | ij_javascript_catch_on_new_line = false 361 | ij_javascript_chained_call_dot_on_new_line = true 362 | ij_javascript_class_brace_style = end_of_line 363 | ij_javascript_comma_on_new_line = false 364 | ij_javascript_do_while_brace_force = never 365 | ij_javascript_else_on_new_line = false 366 | ij_javascript_enforce_trailing_comma = keep 367 | ij_javascript_extends_keyword_wrap = off 368 | ij_javascript_extends_list_wrap = off 369 | ij_javascript_field_prefix = _ 370 | ij_javascript_file_name_style = relaxed 371 | ij_javascript_finally_on_new_line = false 372 | ij_javascript_for_brace_force = never 373 | ij_javascript_for_statement_new_line_after_left_paren = false 374 | ij_javascript_for_statement_right_paren_on_new_line = false 375 | ij_javascript_for_statement_wrap = off 376 | ij_javascript_force_quote_style = false 377 | ij_javascript_force_semicolon_style = false 378 | ij_javascript_function_expression_brace_style = end_of_line 379 | ij_javascript_if_brace_force = never 380 | ij_javascript_import_merge_members = global 381 | ij_javascript_import_prefer_absolute_path = global 382 | ij_javascript_import_sort_members = true 383 | ij_javascript_import_sort_module_name = false 384 | ij_javascript_import_use_node_resolution = true 385 | ij_javascript_imports_wrap = on_every_item 386 | ij_javascript_indent_case_from_switch = true 387 | ij_javascript_indent_chained_calls = true 388 | ij_javascript_indent_package_children = 0 389 | ij_javascript_jsx_attribute_value = braces 390 | ij_javascript_keep_blank_lines_in_code = 2 391 | ij_javascript_keep_first_column_comment = true 392 | ij_javascript_keep_indents_on_empty_lines = false 393 | ij_javascript_keep_line_breaks = true 394 | ij_javascript_keep_simple_blocks_in_one_line = false 395 | ij_javascript_keep_simple_methods_in_one_line = false 396 | ij_javascript_line_comment_add_space = true 397 | ij_javascript_line_comment_at_first_column = false 398 | ij_javascript_method_brace_style = end_of_line 399 | ij_javascript_method_call_chain_wrap = off 400 | ij_javascript_method_parameters_new_line_after_left_paren = false 401 | ij_javascript_method_parameters_right_paren_on_new_line = false 402 | ij_javascript_method_parameters_wrap = off 403 | ij_javascript_object_literal_wrap = on_every_item 404 | ij_javascript_parentheses_expression_new_line_after_left_paren = false 405 | ij_javascript_parentheses_expression_right_paren_on_new_line = false 406 | ij_javascript_place_assignment_sign_on_next_line = false 407 | ij_javascript_prefer_as_type_cast = false 408 | ij_javascript_prefer_explicit_types_function_expression_returns = false 409 | ij_javascript_prefer_explicit_types_function_returns = false 410 | ij_javascript_prefer_explicit_types_vars_fields = false 411 | ij_javascript_prefer_parameters_wrap = false 412 | ij_javascript_reformat_c_style_comments = false 413 | ij_javascript_space_after_colon = true 414 | ij_javascript_space_after_comma = true 415 | ij_javascript_space_after_dots_in_rest_parameter = false 416 | ij_javascript_space_after_generator_mult = false 417 | ij_javascript_space_after_property_colon = true 418 | ij_javascript_space_after_quest = true 419 | ij_javascript_space_after_type_colon = true 420 | ij_javascript_space_after_unary_not = false 421 | ij_javascript_space_before_async_arrow_lparen = true 422 | ij_javascript_space_before_catch_keyword = true 423 | ij_javascript_space_before_catch_left_brace = true 424 | ij_javascript_space_before_catch_parentheses = true 425 | ij_javascript_space_before_class_lbrace = true 426 | ij_javascript_space_before_class_left_brace = true 427 | ij_javascript_space_before_colon = true 428 | ij_javascript_space_before_comma = false 429 | ij_javascript_space_before_do_left_brace = true 430 | ij_javascript_space_before_else_keyword = true 431 | ij_javascript_space_before_else_left_brace = true 432 | ij_javascript_space_before_finally_keyword = true 433 | ij_javascript_space_before_finally_left_brace = true 434 | ij_javascript_space_before_for_left_brace = true 435 | ij_javascript_space_before_for_parentheses = true 436 | ij_javascript_space_before_for_semicolon = false 437 | ij_javascript_space_before_function_left_parenth = true 438 | ij_javascript_space_before_generator_mult = false 439 | ij_javascript_space_before_if_left_brace = true 440 | ij_javascript_space_before_if_parentheses = true 441 | ij_javascript_space_before_method_call_parentheses = false 442 | ij_javascript_space_before_method_left_brace = true 443 | ij_javascript_space_before_method_parentheses = false 444 | ij_javascript_space_before_property_colon = false 445 | ij_javascript_space_before_quest = true 446 | ij_javascript_space_before_switch_left_brace = true 447 | ij_javascript_space_before_switch_parentheses = true 448 | ij_javascript_space_before_try_left_brace = true 449 | ij_javascript_space_before_type_colon = false 450 | ij_javascript_space_before_unary_not = false 451 | ij_javascript_space_before_while_keyword = true 452 | ij_javascript_space_before_while_left_brace = true 453 | ij_javascript_space_before_while_parentheses = true 454 | ij_javascript_spaces_around_additive_operators = true 455 | ij_javascript_spaces_around_arrow_function_operator = true 456 | ij_javascript_spaces_around_assignment_operators = true 457 | ij_javascript_spaces_around_bitwise_operators = true 458 | ij_javascript_spaces_around_equality_operators = true 459 | ij_javascript_spaces_around_logical_operators = true 460 | ij_javascript_spaces_around_multiplicative_operators = true 461 | ij_javascript_spaces_around_relational_operators = true 462 | ij_javascript_spaces_around_shift_operators = true 463 | ij_javascript_spaces_around_unary_operator = false 464 | ij_javascript_spaces_within_array_initializer_brackets = false 465 | ij_javascript_spaces_within_brackets = false 466 | ij_javascript_spaces_within_catch_parentheses = false 467 | ij_javascript_spaces_within_for_parentheses = false 468 | ij_javascript_spaces_within_if_parentheses = false 469 | ij_javascript_spaces_within_imports = true 470 | ij_javascript_spaces_within_interpolation_expressions = false 471 | ij_javascript_spaces_within_method_call_parentheses = false 472 | ij_javascript_spaces_within_method_parentheses = false 473 | ij_javascript_spaces_within_object_literal_braces = true 474 | ij_javascript_spaces_within_object_type_braces = true 475 | ij_javascript_spaces_within_parentheses = false 476 | ij_javascript_spaces_within_switch_parentheses = false 477 | ij_javascript_spaces_within_type_assertion = false 478 | ij_javascript_spaces_within_union_types = true 479 | ij_javascript_spaces_within_while_parentheses = false 480 | ij_javascript_special_else_if_treatment = true 481 | ij_javascript_ternary_operation_signs_on_next_line = false 482 | ij_javascript_ternary_operation_wrap = off 483 | ij_javascript_union_types_wrap = on_every_item 484 | ij_javascript_use_chained_calls_group_indents = false 485 | ij_javascript_use_double_quotes = true 486 | ij_javascript_use_explicit_js_extension = global 487 | ij_javascript_use_path_mapping = always 488 | ij_javascript_use_public_modifier = false 489 | ij_javascript_use_semicolon_after_statement = true 490 | ij_javascript_var_declaration_wrap = normal 491 | ij_javascript_while_brace_force = never 492 | ij_javascript_while_on_new_line = false 493 | ij_javascript_wrap_comments = false 494 | 495 | [{*.cjsx,*.coffee}] 496 | indent_size = 2 497 | tab_width = 2 498 | ij_continuation_indent_size = 2 499 | ij_coffeescript_align_function_body = false 500 | ij_coffeescript_align_imports = false 501 | ij_coffeescript_align_multiline_array_initializer_expression = true 502 | ij_coffeescript_align_multiline_parameters = true 503 | ij_coffeescript_align_multiline_parameters_in_calls = false 504 | ij_coffeescript_align_object_properties = 0 505 | ij_coffeescript_align_union_types = false 506 | ij_coffeescript_align_var_statements = 0 507 | ij_coffeescript_array_initializer_new_line_after_left_brace = false 508 | ij_coffeescript_array_initializer_right_brace_on_new_line = false 509 | ij_coffeescript_array_initializer_wrap = normal 510 | ij_coffeescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** 511 | ij_coffeescript_blank_lines_around_function = 1 512 | ij_coffeescript_call_parameters_new_line_after_left_paren = false 513 | ij_coffeescript_call_parameters_right_paren_on_new_line = false 514 | ij_coffeescript_call_parameters_wrap = normal 515 | ij_coffeescript_chained_call_dot_on_new_line = true 516 | ij_coffeescript_comma_on_new_line = false 517 | ij_coffeescript_enforce_trailing_comma = keep 518 | ij_coffeescript_field_prefix = _ 519 | ij_coffeescript_file_name_style = relaxed 520 | ij_coffeescript_force_quote_style = false 521 | ij_coffeescript_force_semicolon_style = false 522 | ij_coffeescript_function_expression_brace_style = end_of_line 523 | ij_coffeescript_import_merge_members = global 524 | ij_coffeescript_import_prefer_absolute_path = global 525 | ij_coffeescript_import_sort_members = true 526 | ij_coffeescript_import_sort_module_name = false 527 | ij_coffeescript_import_use_node_resolution = true 528 | ij_coffeescript_imports_wrap = on_every_item 529 | ij_coffeescript_indent_chained_calls = true 530 | ij_coffeescript_indent_package_children = 0 531 | ij_coffeescript_jsx_attribute_value = braces 532 | ij_coffeescript_keep_blank_lines_in_code = 2 533 | ij_coffeescript_keep_first_column_comment = true 534 | ij_coffeescript_keep_indents_on_empty_lines = false 535 | ij_coffeescript_keep_line_breaks = true 536 | ij_coffeescript_keep_simple_methods_in_one_line = false 537 | ij_coffeescript_method_parameters_new_line_after_left_paren = false 538 | ij_coffeescript_method_parameters_right_paren_on_new_line = false 539 | ij_coffeescript_method_parameters_wrap = off 540 | ij_coffeescript_object_literal_wrap = on_every_item 541 | ij_coffeescript_prefer_as_type_cast = false 542 | ij_coffeescript_prefer_explicit_types_function_expression_returns = false 543 | ij_coffeescript_prefer_explicit_types_function_returns = false 544 | ij_coffeescript_prefer_explicit_types_vars_fields = false 545 | ij_coffeescript_reformat_c_style_comments = false 546 | ij_coffeescript_space_after_comma = true 547 | ij_coffeescript_space_after_dots_in_rest_parameter = false 548 | ij_coffeescript_space_after_generator_mult = true 549 | ij_coffeescript_space_after_property_colon = true 550 | ij_coffeescript_space_after_type_colon = true 551 | ij_coffeescript_space_after_unary_not = false 552 | ij_coffeescript_space_before_async_arrow_lparen = true 553 | ij_coffeescript_space_before_class_lbrace = true 554 | ij_coffeescript_space_before_comma = false 555 | ij_coffeescript_space_before_function_left_parenth = true 556 | ij_coffeescript_space_before_generator_mult = false 557 | ij_coffeescript_space_before_property_colon = false 558 | ij_coffeescript_space_before_type_colon = false 559 | ij_coffeescript_space_before_unary_not = false 560 | ij_coffeescript_spaces_around_additive_operators = true 561 | ij_coffeescript_spaces_around_arrow_function_operator = true 562 | ij_coffeescript_spaces_around_assignment_operators = true 563 | ij_coffeescript_spaces_around_bitwise_operators = true 564 | ij_coffeescript_spaces_around_equality_operators = true 565 | ij_coffeescript_spaces_around_logical_operators = true 566 | ij_coffeescript_spaces_around_multiplicative_operators = true 567 | ij_coffeescript_spaces_around_relational_operators = true 568 | ij_coffeescript_spaces_around_shift_operators = true 569 | ij_coffeescript_spaces_around_unary_operator = false 570 | ij_coffeescript_spaces_within_array_initializer_braces = false 571 | ij_coffeescript_spaces_within_array_initializer_brackets = false 572 | ij_coffeescript_spaces_within_imports = false 573 | ij_coffeescript_spaces_within_index_brackets = false 574 | ij_coffeescript_spaces_within_interpolation_expressions = false 575 | ij_coffeescript_spaces_within_method_call_parentheses = false 576 | ij_coffeescript_spaces_within_method_parentheses = false 577 | ij_coffeescript_spaces_within_object_braces = false 578 | ij_coffeescript_spaces_within_object_literal_braces = false 579 | ij_coffeescript_spaces_within_object_type_braces = true 580 | ij_coffeescript_spaces_within_range_brackets = false 581 | ij_coffeescript_spaces_within_type_assertion = false 582 | ij_coffeescript_spaces_within_union_types = true 583 | ij_coffeescript_union_types_wrap = on_every_item 584 | ij_coffeescript_use_chained_calls_group_indents = false 585 | ij_coffeescript_use_double_quotes = true 586 | ij_coffeescript_use_explicit_js_extension = global 587 | ij_coffeescript_use_path_mapping = always 588 | ij_coffeescript_use_public_modifier = false 589 | ij_coffeescript_use_semicolon_after_statement = false 590 | ij_coffeescript_var_declaration_wrap = normal 591 | 592 | [{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}] 593 | ij_continuation_indent_size = 4 594 | ij_php_align_assignments = true 595 | ij_php_align_class_constants = true 596 | ij_php_align_group_field_declarations = true 597 | ij_php_align_inline_comments = false 598 | ij_php_align_key_value_pairs = true 599 | ij_php_align_multiline_array_initializer_expression = true 600 | ij_php_align_multiline_binary_operation = false 601 | ij_php_align_multiline_chained_methods = false 602 | ij_php_align_multiline_extends_list = true 603 | ij_php_align_multiline_for = true 604 | ij_php_align_multiline_parameters = true 605 | ij_php_align_multiline_parameters_in_calls = false 606 | ij_php_align_multiline_ternary_operation = false 607 | ij_php_align_phpdoc_comments = true 608 | ij_php_align_phpdoc_param_names = true 609 | ij_php_anonymous_brace_style = end_of_line 610 | ij_php_api_weight = 28 611 | ij_php_array_initializer_new_line_after_left_brace = false 612 | ij_php_array_initializer_right_brace_on_new_line = false 613 | ij_php_array_initializer_wrap = off 614 | ij_php_assignment_wrap = off 615 | ij_php_attributes_wrap = off 616 | ij_php_author_weight = 28 617 | ij_php_binary_operation_sign_on_next_line = false 618 | ij_php_binary_operation_wrap = off 619 | ij_php_blank_lines_after_class_header = 0 620 | ij_php_blank_lines_after_function = 1 621 | ij_php_blank_lines_after_imports = 2 622 | ij_php_blank_lines_after_opening_tag = 0 623 | ij_php_blank_lines_after_package = 0 624 | ij_php_blank_lines_around_class = 1 625 | ij_php_blank_lines_around_constants = 0 626 | ij_php_blank_lines_around_field = 0 627 | ij_php_blank_lines_around_method = 1 628 | ij_php_blank_lines_before_class_end = 0 629 | ij_php_blank_lines_before_imports = 1 630 | ij_php_blank_lines_before_method_body = 0 631 | ij_php_blank_lines_before_package = 1 632 | ij_php_blank_lines_before_return_statement = 1 633 | ij_php_blank_lines_between_imports = 0 634 | ij_php_block_brace_style = end_of_line 635 | ij_php_call_parameters_new_line_after_left_paren = false 636 | ij_php_call_parameters_right_paren_on_new_line = false 637 | ij_php_call_parameters_wrap = off 638 | ij_php_catch_on_new_line = false 639 | ij_php_category_weight = 28 640 | ij_php_class_brace_style = next_line 641 | ij_php_comma_after_last_array_element = true 642 | ij_php_concat_spaces = true 643 | ij_php_copyright_weight = 28 644 | ij_php_deprecated_weight = 28 645 | ij_php_do_while_brace_force = never 646 | ij_php_else_if_style = combine 647 | ij_php_else_on_new_line = false 648 | ij_php_example_weight = 28 649 | ij_php_extends_keyword_wrap = off 650 | ij_php_extends_list_wrap = off 651 | ij_php_fields_default_visibility = private 652 | ij_php_filesource_weight = 28 653 | ij_php_finally_on_new_line = false 654 | ij_php_for_brace_force = never 655 | ij_php_for_statement_new_line_after_left_paren = false 656 | ij_php_for_statement_right_paren_on_new_line = false 657 | ij_php_for_statement_wrap = off 658 | ij_php_force_short_declaration_array_style = true 659 | ij_php_getters_setters_naming_style = camel_case 660 | ij_php_getters_setters_order_style = getters_first 661 | ij_php_global_weight = 28 662 | ij_php_group_use_wrap = on_every_item 663 | ij_php_if_brace_force = never 664 | ij_php_if_lparen_on_next_line = false 665 | ij_php_if_rparen_on_next_line = false 666 | ij_php_ignore_weight = 28 667 | ij_php_import_sorting = alphabetic 668 | ij_php_indent_break_from_case = true 669 | ij_php_indent_case_from_switch = true 670 | ij_php_indent_code_in_php_tags = false 671 | ij_php_internal_weight = 28 672 | ij_php_keep_blank_lines_after_lbrace = 2 673 | ij_php_keep_blank_lines_before_right_brace = 2 674 | ij_php_keep_blank_lines_in_code = 2 675 | ij_php_keep_blank_lines_in_declarations = 2 676 | ij_php_keep_control_statement_in_one_line = true 677 | ij_php_keep_first_column_comment = true 678 | ij_php_keep_indents_on_empty_lines = false 679 | ij_php_keep_line_breaks = true 680 | ij_php_keep_rparen_and_lbrace_on_one_line = true 681 | ij_php_keep_simple_classes_in_one_line = false 682 | ij_php_keep_simple_methods_in_one_line = false 683 | ij_php_lambda_brace_style = end_of_line 684 | ij_php_license_weight = 28 685 | ij_php_line_comment_add_space = false 686 | ij_php_line_comment_at_first_column = true 687 | ij_php_link_weight = 28 688 | ij_php_lower_case_boolean_const = true 689 | ij_php_lower_case_keywords = true 690 | ij_php_lower_case_null_const = true 691 | ij_php_method_brace_style = next_line 692 | ij_php_method_call_chain_wrap = off 693 | ij_php_method_parameters_new_line_after_left_paren = false 694 | ij_php_method_parameters_right_paren_on_new_line = false 695 | ij_php_method_parameters_wrap = off 696 | ij_php_method_weight = 28 697 | ij_php_modifier_list_wrap = false 698 | ij_php_multiline_chained_calls_semicolon_on_new_line = false 699 | ij_php_namespace_brace_style = 1 700 | ij_php_new_line_after_php_opening_tag = false 701 | ij_php_null_type_position = in_the_end 702 | ij_php_package_weight = 28 703 | ij_php_param_weight = 0 704 | ij_php_parameters_attributes_wrap = off 705 | ij_php_parentheses_expression_new_line_after_left_paren = false 706 | ij_php_parentheses_expression_right_paren_on_new_line = false 707 | ij_php_phpdoc_blank_line_before_tags = true 708 | ij_php_phpdoc_blank_lines_around_parameters = true 709 | ij_php_phpdoc_keep_blank_lines = true 710 | ij_php_phpdoc_param_spaces_between_name_and_description = 1 711 | ij_php_phpdoc_param_spaces_between_tag_and_type = 1 712 | ij_php_phpdoc_param_spaces_between_type_and_name = 1 713 | ij_php_phpdoc_use_fqcn = false 714 | ij_php_phpdoc_wrap_long_lines = false 715 | ij_php_place_assignment_sign_on_next_line = false 716 | ij_php_place_parens_for_constructor = 0 717 | ij_php_property_read_weight = 28 718 | ij_php_property_weight = 28 719 | ij_php_property_write_weight = 28 720 | ij_php_return_type_on_new_line = false 721 | ij_php_return_weight = 1 722 | ij_php_see_weight = 28 723 | ij_php_since_weight = 28 724 | ij_php_sort_phpdoc_elements = true 725 | ij_php_space_after_colon = true 726 | ij_php_space_after_colon_in_named_argument = true 727 | ij_php_space_after_colon_in_return_type = true 728 | ij_php_space_after_comma = true 729 | ij_php_space_after_for_semicolon = true 730 | ij_php_space_after_quest = true 731 | ij_php_space_after_type_cast = false 732 | ij_php_space_after_unary_not = true 733 | ij_php_space_before_array_initializer_left_brace = false 734 | ij_php_space_before_catch_keyword = true 735 | ij_php_space_before_catch_left_brace = true 736 | ij_php_space_before_catch_parentheses = true 737 | ij_php_space_before_class_left_brace = true 738 | ij_php_space_before_closure_left_parenthesis = true 739 | ij_php_space_before_colon = true 740 | ij_php_space_before_colon_in_named_argument = false 741 | ij_php_space_before_colon_in_return_type = false 742 | ij_php_space_before_comma = false 743 | ij_php_space_before_do_left_brace = true 744 | ij_php_space_before_else_keyword = true 745 | ij_php_space_before_else_left_brace = true 746 | ij_php_space_before_finally_keyword = true 747 | ij_php_space_before_finally_left_brace = true 748 | ij_php_space_before_for_left_brace = true 749 | ij_php_space_before_for_parentheses = true 750 | ij_php_space_before_for_semicolon = false 751 | ij_php_space_before_if_left_brace = true 752 | ij_php_space_before_if_parentheses = true 753 | ij_php_space_before_method_call_parentheses = false 754 | ij_php_space_before_method_left_brace = true 755 | ij_php_space_before_method_parentheses = false 756 | ij_php_space_before_quest = true 757 | ij_php_space_before_short_closure_left_parenthesis = false 758 | ij_php_space_before_switch_left_brace = true 759 | ij_php_space_before_switch_parentheses = true 760 | ij_php_space_before_try_left_brace = true 761 | ij_php_space_before_unary_not = false 762 | ij_php_space_before_while_keyword = true 763 | ij_php_space_before_while_left_brace = true 764 | ij_php_space_before_while_parentheses = true 765 | ij_php_space_between_ternary_quest_and_colon = false 766 | ij_php_spaces_around_additive_operators = true 767 | ij_php_spaces_around_arrow = false 768 | ij_php_spaces_around_assignment_in_declare = false 769 | ij_php_spaces_around_assignment_operators = true 770 | ij_php_spaces_around_bitwise_operators = true 771 | ij_php_spaces_around_equality_operators = true 772 | ij_php_spaces_around_logical_operators = true 773 | ij_php_spaces_around_multiplicative_operators = true 774 | ij_php_spaces_around_null_coalesce_operator = true 775 | ij_php_spaces_around_relational_operators = true 776 | ij_php_spaces_around_shift_operators = true 777 | ij_php_spaces_around_unary_operator = false 778 | ij_php_spaces_around_var_within_brackets = false 779 | ij_php_spaces_within_array_initializer_braces = false 780 | ij_php_spaces_within_brackets = false 781 | ij_php_spaces_within_catch_parentheses = false 782 | ij_php_spaces_within_for_parentheses = false 783 | ij_php_spaces_within_if_parentheses = false 784 | ij_php_spaces_within_method_call_parentheses = false 785 | ij_php_spaces_within_method_parentheses = false 786 | ij_php_spaces_within_parentheses = false 787 | ij_php_spaces_within_short_echo_tags = true 788 | ij_php_spaces_within_switch_parentheses = false 789 | ij_php_spaces_within_while_parentheses = false 790 | ij_php_special_else_if_treatment = false 791 | ij_php_subpackage_weight = 28 792 | ij_php_ternary_operation_signs_on_next_line = false 793 | ij_php_ternary_operation_wrap = off 794 | ij_php_throws_weight = 2 795 | ij_php_todo_weight = 28 796 | ij_php_unknown_tag_weight = 28 797 | ij_php_upper_case_boolean_const = false 798 | ij_php_upper_case_null_const = false 799 | ij_php_uses_weight = 28 800 | ij_php_var_weight = 28 801 | ij_php_variable_naming_style = mixed 802 | ij_php_version_weight = 28 803 | ij_php_while_brace_force = never 804 | ij_php_while_on_new_line = false 805 | 806 | [{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}] 807 | indent_size = 2 808 | ij_json_keep_blank_lines_in_code = 0 809 | ij_json_keep_indents_on_empty_lines = false 810 | ij_json_keep_line_breaks = true 811 | ij_json_space_after_colon = true 812 | ij_json_space_after_comma = true 813 | ij_json_space_before_colon = true 814 | ij_json_space_before_comma = false 815 | ij_json_spaces_within_braces = false 816 | ij_json_spaces_within_brackets = false 817 | ij_json_wrap_long_lines = false 818 | 819 | [{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml,index.ejs}] 820 | ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 821 | ij_html_align_attributes = true 822 | ij_html_align_text = false 823 | ij_html_attribute_wrap = normal 824 | ij_html_block_comment_at_first_column = true 825 | ij_html_do_not_align_children_of_min_lines = 0 826 | ij_html_do_not_break_if_inline_tags = title,h1,h2,h3,h4,h5,h6,p 827 | ij_html_do_not_indent_children_of_tags = html,body,thead,tbody,tfoot 828 | ij_html_enforce_quotes = false 829 | ij_html_inline_tags = a,abbr,acronym,b,basefont,bdo,big,br,cite,cite,code,dfn,em,font,i,img,input,kbd,label,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var 830 | ij_html_keep_blank_lines = 2 831 | ij_html_keep_indents_on_empty_lines = false 832 | ij_html_keep_line_breaks = true 833 | ij_html_keep_line_breaks_in_text = true 834 | ij_html_keep_whitespaces = false 835 | ij_html_keep_whitespaces_inside = span,pre,textarea 836 | ij_html_line_comment_at_first_column = true 837 | ij_html_new_line_after_last_attribute = never 838 | ij_html_new_line_before_first_attribute = never 839 | ij_html_quote_style = double 840 | ij_html_remove_new_line_before_tags = br 841 | ij_html_space_after_tag_name = false 842 | ij_html_space_around_equality_in_attribute = false 843 | ij_html_space_inside_empty_tag = true 844 | ij_html_text_wrap = normal 845 | ij_html_uniform_ident = false 846 | 847 | [{*.markdown,*.md}] 848 | ij_markdown_force_one_space_after_blockquote_symbol = true 849 | ij_markdown_force_one_space_after_header_symbol = true 850 | ij_markdown_force_one_space_after_list_bullet = true 851 | ij_markdown_force_one_space_between_words = true 852 | ij_markdown_keep_indents_on_empty_lines = false 853 | ij_markdown_max_lines_around_block_elements = 1 854 | ij_markdown_max_lines_around_header = 1 855 | ij_markdown_max_lines_between_paragraphs = 1 856 | ij_markdown_min_lines_around_block_elements = 1 857 | ij_markdown_min_lines_around_header = 1 858 | ij_markdown_min_lines_between_paragraphs = 1 859 | 860 | [{*.yaml,*.yml}] 861 | indent_size = 2 862 | ij_yaml_align_values_properties = do_not_align 863 | ij_yaml_autoinsert_sequence_marker = true 864 | ij_yaml_block_mapping_on_new_line = false 865 | ij_yaml_indent_sequence_value = true 866 | ij_yaml_keep_indents_on_empty_lines = false 867 | ij_yaml_keep_line_breaks = true 868 | ij_yaml_sequence_on_new_line = false 869 | ij_yaml_space_before_colon = false 870 | ij_yaml_spaces_within_braces = true 871 | ij_yaml_spaces_within_brackets = true 872 | --------------------------------------------------------------------------------