├── docs ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── docusaurus.png │ │ ├── paymongo-logo.png │ │ ├── tutorial │ │ ├── localeDropdown.png │ │ └── docsVersionDropdown.png │ │ └── logo.svg ├── babel.config.js ├── .gitignore ├── sidebars.js ├── README.md ├── docs │ ├── Usage │ │ ├── sources.md │ │ ├── tokens.md │ │ ├── payment-methods.md │ │ ├── payments.md │ │ ├── links.md │ │ ├── refunds.md │ │ ├── customers.md │ │ ├── checkouts.md │ │ ├── payment-intents.md │ │ └── webhooks.md │ └── getting-started.md ├── package.json └── docusaurus.config.js ├── .styleci.yml ├── src ├── Models │ ├── Payment.php │ ├── Source.php │ ├── Token.php │ ├── PaymentMethod.php │ ├── Checkout.php │ ├── Refund.php │ ├── Link.php │ ├── PaymentIntent.php │ ├── Webhook.php │ ├── Customer.php │ └── BaseModel.php ├── Signer │ ├── Signer.php │ └── DefaultSigner.php ├── Exceptions │ ├── NotFoundException.php │ ├── MethodNotFoundException.php │ ├── PaymentErrorException.php │ ├── AmountTypeNotSupportedException.php │ ├── BadRequestException.php │ └── UnauthorizedException.php ├── Traits │ ├── HasCommandValidation.php │ ├── HasWebhooksTable.php │ ├── HasToggleWebhook.php │ └── Request.php ├── Commands │ ├── WebhookListCommand.php │ ├── WebhookAddCommand.php │ └── WebhookToggleCommand.php ├── Facades │ └── Paymongo.php ├── PaymongoServiceProvider.php ├── Middlewares │ └── PaymongoValidateSignature.php └── Paymongo.php ├── .github ├── workflows │ ├── update-changelog.yml │ └── test.yml └── ISSUE_TEMPLATE │ ├── ---ideas.md │ └── ---bug-report.md ├── CHANGELOG.md ├── LICENSE.md ├── .php_cs.dist ├── composer.json ├── README.md ├── config └── config.php ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md └── .phpunit.result.cache /docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luigel/laravel-paymongo/HEAD/docs/static/img/favicon.ico -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | 3 | version: 8.0 4 | 5 | disabled: 6 | - single_class_element_per_statement 7 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luigel/laravel-paymongo/HEAD/docs/static/img/docusaurus.png -------------------------------------------------------------------------------- /docs/static/img/paymongo-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luigel/laravel-paymongo/HEAD/docs/static/img/paymongo-logo.png -------------------------------------------------------------------------------- /src/Models/Payment.php: -------------------------------------------------------------------------------- 1 | checkout()->expireCheckout($this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /src/Models/Refund.php: -------------------------------------------------------------------------------- 1 | link()->archive($this); 12 | } 13 | 14 | public function unarchive(): BaseModel 15 | { 16 | return (new Paymongo)->link()->unarchive($this); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Exceptions/NotFoundException.php: -------------------------------------------------------------------------------- 1 | paymentIntent()->cancel($this); 12 | } 13 | 14 | public function attach(string $paymentMethodId, ?string $returnUrl = null): BaseModel 15 | { 16 | return (new Paymongo)->paymentIntent()->attach($this, $paymentMethodId, $returnUrl); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Traits/HasCommandValidation.php: -------------------------------------------------------------------------------- 1 | fails()) { 14 | $this->info($message); 15 | 16 | foreach ($validator->errors()->all() as $error) { 17 | $this->error($error); 18 | } 19 | 20 | return 1; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Signer/DefaultSigner.php: -------------------------------------------------------------------------------- 1 | webhook()->enable($this); 14 | } 15 | 16 | public function disable(): BaseModel 17 | { 18 | return (new Paymongo)->webhook()->disable($this); 19 | } 20 | 21 | public function update(array $payload): BaseModel 22 | { 23 | return (new Paymongo)->webhook()->update($this, $payload); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Models/Customer.php: -------------------------------------------------------------------------------- 1 | customer()->updateCustomer($this, $payload); 13 | } 14 | 15 | public function delete(): BaseModel 16 | { 17 | return (new Paymongo)->customer()->deleteCustomer($this); 18 | } 19 | 20 | public function paymentMethods(): BaseModel|Collection 21 | { 22 | return (new Paymongo)->customer()->getPaymentMethods($this); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | module.exports = { 13 | // By default, Docusaurus generates a sidebar from the docs folder structure 14 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 15 | 16 | // But you can create a sidebar manually 17 | /* 18 | tutorialSidebar: [ 19 | { 20 | type: 'category', 21 | label: 'Tutorial', 22 | items: ['hello'], 23 | }, 24 | ], 25 | */ 26 | }; 27 | -------------------------------------------------------------------------------- /src/Commands/WebhookListCommand.php: -------------------------------------------------------------------------------- 1 | displayWebhooks(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /.github/workflows/update-changelog.yml: -------------------------------------------------------------------------------- 1 | name: "Update Changelog" 2 | 3 | on: 4 | release: 5 | types: [released] 6 | 7 | jobs: 8 | update: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout code 13 | uses: actions/checkout@v2 14 | with: 15 | ref: 2.x 16 | 17 | - name: Update Changelog 18 | uses: stefanzweifel/changelog-updater-action@v1 19 | with: 20 | latest-version: ${{ github.event.release.name }} 21 | release-notes: ${{ github.event.release.body }} 22 | 23 | - name: Commit updated CHANGELOG 24 | uses: stefanzweifel/git-auto-commit-action@v4 25 | with: 26 | branch: main 27 | commit_message: Update CHANGELOG 28 | file_pattern: CHANGELOG.md 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-paymongo` will be documented in this file 4 | 5 | ## 2.4.0 (2023-04-30) 6 | 7 | ### Added 8 | - Checkouts API 9 | - Support laravel 10 10 | 11 | ## 2.3.0 (2022-12-15) 12 | 13 | ### Added 14 | - Links API 15 | - Customers API 16 | 17 | ### Fixed 18 | - Failing tests 19 | 20 | ## 1.3.0 (2020-10-31) 21 | 22 | ### Added 23 | 24 | - Added `getData()` for all the models. 25 | - It can now access the properties of the data directly. (eg. `$token->id`) 26 | - Added magic methods to all the models to get the specific data. Using the `get` prefix for the method. 27 | 28 | #### Example: 29 | 30 | ```php 31 | // Get the ID of the token. 32 | $id = $token->getId(); 33 | 34 | // Get the billing details of the payment method. 35 | $paymentMethod->getBilling(); 36 | 37 | // Get the billing name of the payment method. 38 | $paymentMethod->getBillingName(); 39 | ``` 40 | 41 | - Added artisan commands for webhooks. 42 | -------------------------------------------------------------------------------- /src/Facades/Paymongo.php: -------------------------------------------------------------------------------- 1 | table($headers, $this->webhooks($headers)); 19 | } 20 | 21 | /** 22 | * Get all the webhooks. 23 | * 24 | * @param array $headers 25 | * @return \Illuminate\Support\Collection 26 | */ 27 | protected function webhooks($headers) 28 | { 29 | return Paymongo::webhook()->all()->map(function ($webhook) use ($headers) { 30 | return collect($webhook->getData())->only($headers)->toArray(); 31 | })->map(function ($webhook) { 32 | $webhook['livemode'] = $webhook['livemode'] ? 'YES' : 'NO'; 33 | 34 | return $webhook; 35 | })->toArray(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/docs/Usage/sources.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | slug: /sources 4 | id: sources 5 | --- 6 | 7 | # Sources 8 | 9 | ## Create Source 10 | 11 | Creates a source to let the user pay using their [Gcash Accounts](https://www.gcash.com) or [Grab Pay Accounts](https://www.grab.com/ph/pay/). 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/the-sources-object) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $gcashSource = Paymongo::source()->create([ 23 | 'type' => 'gcash', 24 | 'amount' => 100.00, 25 | 'currency' => 'PHP', 26 | 'redirect' => [ 27 | 'success' => 'https://your-domain.com/success', 28 | 'failed' => 'https://your-domain.com/failed' 29 | ] 30 | ]); 31 | 32 | $grabCarSource = Paymongo::source()->create([ 33 | 'type' => 'grab_pay', 34 | 'amount' => 100.00, 35 | 'currency' => 'PHP', 36 | 'redirect' => [ 37 | 'success' => 'https://your-domain.com/success', 38 | 'failed' => 'https://your-domain.com/failed' 39 | ] 40 | ]); 41 | ``` 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Rigel Kent Carbonel 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "2.0.0-beta.6", 18 | "@docusaurus/preset-classic": "2.0.0-beta.6", 19 | "@mdx-js/react": "^1.6.21", 20 | "@svgr/webpack": "^5.5.0", 21 | "clsx": "^1.1.1", 22 | "file-loader": "^6.2.0", 23 | "prism-react-renderer": "^1.2.1", 24 | "react": "^17.0.1", 25 | "react-dom": "^17.0.1", 26 | "url-loader": "^4.1.1" 27 | }, 28 | "browserslist": { 29 | "production": [ 30 | ">0.5%", 31 | "not dead", 32 | "not op_mini all" 33 | ], 34 | "development": [ 35 | "last 1 chrome version", 36 | "last 1 firefox version", 37 | "last 1 safari version" 38 | ] 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Traits/HasToggleWebhook.php: -------------------------------------------------------------------------------- 1 | method = 'POST'; 19 | $this->apiUrl = $this->apiUrl.$webhook->getId().'/enable'; 20 | 21 | $this->setGuzzleOptions([ 22 | 'headers' => [ 23 | 'Accept' => 'application/json', 24 | ], 25 | 'auth' => [config('paymongo.secret_key'), ''], 26 | ]); 27 | 28 | return $this->request(); 29 | } 30 | 31 | /** 32 | * Disables the webhook. 33 | * 34 | * @param Webhook $webhook 35 | * @return BaseModel 36 | */ 37 | public function disable(Webhook $webhook) 38 | { 39 | $this->method = 'POST'; 40 | $this->apiUrl = $this->apiUrl.$webhook->getId().'/disable'; 41 | 42 | $this->setGuzzleOptions([ 43 | 'headers' => [ 44 | 'Accept' => 'application/json', 45 | ], 46 | 'auth' => [config('paymongo.secret_key'), ''], 47 | ]); 48 | 49 | return $this->request(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---ideas.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4A1 Ideas" 3 | about: "If you have a \U0001F525 idea or proposals." 4 | title: "[IDEA]" 5 | labels: enhancement 6 | assignees: luigel 7 | 8 | --- 9 | 10 | 23 | 24 | ### Prerequisites 25 | 26 | ### Versions 27 | 28 | 29 | 30 | * PHP version: 31 | * Laravel version: 32 | * Package version: 33 | 34 | ### Description 35 | 36 | 37 | 38 | ### Example 39 | 40 | 41 | 42 | ### Additional Information 43 | 44 | Any additional information, configuration or data that might be needed to understand the proposal. 45 | -------------------------------------------------------------------------------- /docs/docs/Usage/tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | slug: /tokens 4 | id: tokens 5 | --- 6 | 7 | # Tokens 8 | 9 | :::caution 10 | **Tokens API has been deprecated.** 11 | ::: 12 | 13 | ## Create Token 14 | 15 | Creates a one-time use token representing your customer's credit card details. NOTE: This token can only be used once to create a Payment. You must create separate tokens for every payment attempt. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $token = Paymongo::token() 23 | ->create([ 24 | 'number' => '4343434343434345', 25 | 'exp_month' => 12, 26 | 'exp_year' => 25, 27 | 'cvc' => "123", 28 | 'billing' => [ 29 | 'address' => [ 30 | 'line1' => 'Test Address', 31 | 'city' => 'Cebu City', 32 | 'postal_code' => '6000', 33 | 'country' => 'PH' 34 | ], 35 | 'name' => 'Rigel Kent Carbonel', 36 | 'email' => 'rigel20.kent@gmail.com', 37 | 'phone' => '928392893' 38 | ] 39 | ]); 40 | ``` 41 | 42 | ## Get Token 43 | 44 | Retrieve a token given an ID. The prefix for the id is `tok_` followed by a unique hash representing the token. Just pass the token id to `find($id)` method. 45 | 46 | ### Sample 47 | 48 | ```php 49 | use Luigel\Paymongo\Facades\Paymongo; 50 | 51 | $token = Paymongo::token()->find($tokenId); 52 | ``` 53 | -------------------------------------------------------------------------------- /docs/docs/Usage/payment-methods.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: /payment-methods 4 | id: payment-methods 5 | --- 6 | 7 | # Payment Methods 8 | 9 | ## Create Payment Method 10 | 11 | Creates a payment methods. It holds the information such as credit card information and billing information. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/the-payment-method-object) for payload. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $paymentMethod = Paymongo::paymentMethod()->create([ 23 | 'type' => 'card', 24 | 'details' => [ 25 | 'card_number' => '4343434343434345', 26 | 'exp_month' => 12, 27 | 'exp_year' => 25, 28 | 'cvc' => "123", 29 | ], 30 | 'billing' => [ 31 | 'address' => [ 32 | 'line1' => 'Somewhere there', 33 | 'city' => 'Cebu City', 34 | 'state' => 'Cebu', 35 | 'country' => 'PH', 36 | 'postal_code' => '6000', 37 | ], 38 | 'name' => 'Rigel Kent Carbonel', 39 | 'email' => 'rigel20.kent@gmail.com', 40 | 'phone' => '0935454875545' 41 | ], 42 | ]); 43 | ``` 44 | 45 | ## Get Payment Method 46 | 47 | Retrieve a payment method given an ID. Just pass the payment method id to `find($id)` method. 48 | 49 | ### Sample 50 | 51 | ```php 52 | use Luigel\Paymongo\Facades\Paymongo; 53 | 54 | $paymentMethod = Paymongo::paymentMethod()->find('pm_wr98R2gwWroVxfkcNVZBuXg2'); 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/docs/Usage/payments.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | slug: /payments 4 | id: payments 5 | --- 6 | 7 | # Payments 8 | 9 | ## Create Payment 10 | 11 | Creates a payment using source. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/payment-source) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $payment = Paymongo::payment() 23 | ->create([ 24 | 'amount' => 100.00, 25 | 'currency' => 'PHP', 26 | 'description' => 'Testing payment', 27 | 'statement_descriptor' => 'Test Paymongo', 28 | 'source' => [ 29 | 'id' => $source->id, 30 | 'type' => 'source' 31 | ] 32 | ]); 33 | ``` 34 | 35 | ## Get Payment 36 | 37 | You can retrieve a Payment by providing a payment ID. The prefix for the id is `pay_` followed by a unique hash representing the payment. Just pass the payment id to `find($paymentId)` method. 38 | 39 | ### Sample 40 | 41 | ```php 42 | use Luigel\Paymongo\Facades\Paymongo; 43 | 44 | $payment = Paymongo::payment()->find('pay_i35wBzLNdX8i9nKEPaSKWGib'); 45 | ``` 46 | 47 | ## Get All Payments 48 | 49 | Returns all the payments you previously created, with the most recent payments returned first. Currently, all payments are returned as one batch. We will be introducing pagination and limits in the next iteration of the API. 50 | 51 | ### Sample 52 | 53 | ```php 54 | use Luigel\Paymongo\Facades\Paymongo; 55 | 56 | $payments = Paymongo::payment()->all(); 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/docs/Usage/links.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | slug: /links 4 | id: links 5 | --- 6 | 7 | # Links 8 | 9 | ## Create Link 10 | 11 | Creates a payment link. A payment link that can be used for one-time payments. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/links-resource) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $link = Paymongo::link()->create([ 23 | 'amount' => 100.00, 24 | 'description' => 'Link Test', 25 | 'remarks' => 'laravel-paymongo' 26 | ]); 27 | ``` 28 | 29 | ## Get Link 30 | 31 | Retrieve a payment link by passing the id or the reference number to the `find($id)` method. 32 | 33 | ### Sample 34 | 35 | ```php 36 | use Luigel\Paymongo\Facades\Paymongo; 37 | 38 | $link = Paymongo::link()->find('link_wWaibr22CzEnficNhQNPUdoo'); 39 | $linkbyReference = Paymongo::link()->find('WTmSJbV'); 40 | ``` 41 | 42 | ## Archive Link 43 | 44 | Archive a payment link by using the `find($id)` method and chaining the `->archive()` method. 45 | 46 | ### Sample 47 | 48 | ```php 49 | use Luigel\Paymongo\Facades\Paymongo; 50 | 51 | $link = Paymongo::link()->find('link_wWaibr22CzEnficNhQNPUdoo')->archive(); 52 | ``` 53 | 54 | ## Unarchive Link 55 | 56 | Unarchive a payment link by using the `find($id)` method and chaining the `->unarchive()` method. 57 | 58 | ### Sample 59 | 60 | ```php 61 | use Luigel\Paymongo\Facades\Paymongo; 62 | 63 | $link = Paymongo::link()->find('link_wWaibr22CzEnficNhQNPUdoo')->unarchive(); 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /src/PaymongoServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 20 | $this->publishes([ 21 | __DIR__.'/../config/config.php' => config_path('paymongo.php'), 22 | ], 'config'); 23 | } 24 | } 25 | 26 | /** 27 | * Register the application services. 28 | */ 29 | public function register() 30 | { 31 | if ($this->app->runningInConsole()) { 32 | $this->commands([ 33 | WebhookListCommand::class, 34 | WebhookAddCommand::class, 35 | WebhookToggleCommand::class, 36 | ]); 37 | } 38 | 39 | $this->mergeConfigFrom(__DIR__.'/../config/config.php', 'paymongo'); 40 | 41 | $this->app->singleton('paymongo', function () { 42 | return new Paymongo; 43 | }); 44 | 45 | $this->app->bind(Signer::class, config('paymongo.signer')); 46 | 47 | $this->app['router']->aliasMiddleware('paymongo.signature', PaymongoValidateSignature::class); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/docs/Usage/refunds.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 9 3 | slug: /refunds 4 | id: refunds 5 | --- 6 | 7 | # Refunds 8 | 9 | ## Create Refund 10 | 11 | Performs a refund to a customer's paid payments in full or a partial amount to the original payment method used. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/refund-resource) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | Here are the possible values of the reasons. 20 | - \Luigel\Paymongo\Models\Refund::REASON_DUPLICATE 21 | - \Luigel\Paymongo\Models\Refund::REASON_FRAUDULENT 22 | - \Luigel\Paymongo\Models\Refund::REASON_REQUESTED_BY_CUSTOMER 23 | - \Luigel\Paymongo\Models\Refund::REASON_OTHERS 24 | 25 | ```php 26 | use Luigel\Paymongo\Facades\Paymongo; 27 | 28 | $refund = Paymongo::refund()->create([ 29 | 'amount' => 10, 30 | 'notes' => 'test refund', 31 | 'payment_id' => $payment->id, 32 | 'reason' => \Luigel\Paymongo\Models\Refund::REASON_DUPLICATE, 33 | ]); 34 | ``` 35 | 36 | ## Get Refund 37 | 38 | You can retrieve a Refund by providing a refund ID. The prefix for the id is `ref_` followed by a unique hash representing the payment. Just pass the refund id to `find($refundId)` method. 39 | 40 | ### Sample 41 | 42 | ```php 43 | use Luigel\Paymongo\Facades\Paymongo; 44 | 45 | $payment = Paymongo::refund()->find('ref_rBCmgwgMXZ9VH4YS2eRooPVL'); 46 | ``` 47 | 48 | ## Get All Refunds 49 | 50 | Returns all the refunds you previously created, with the most recent refunds returned first. 51 | 52 | ### Sample 53 | 54 | ```php 55 | use Luigel\Paymongo\Facades\Paymongo; 56 | 57 | $payments = Paymongo::refund()->all(); 58 | ``` 59 | -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | notPath('bootstrap') 5 | ->notPath('storage') 6 | ->notPath('vendor') 7 | ->in(__DIR__) 8 | ->name('*.php') 9 | ->notName('*.blade.php'); 10 | 11 | return PhpCsFixer\Config::create() 12 | ->setRules([ 13 | '@PSR2' => true, 14 | 'array_syntax' => ['syntax' => 'short'], 15 | 'not_operator_with_successor_space' => true, 16 | 'no_extra_blank_lines' => [ 17 | 'curly_brace_block', 18 | 'extra', 19 | 'parenthesis_brace_block', 20 | 'throw', 21 | 'use', 22 | ], 23 | 'no_unused_imports' => true, 24 | 'ordered_imports' => ['sortAlgorithm' => 'alpha'], 25 | 'ternary_operator_spaces' => true, 26 | 'single_blank_line_before_namespace' => true, 27 | 28 | // PSR-12 29 | 'blank_line_after_opening_tag' => true, 30 | 'braces' => ['allow_single_line_closure' => true], 31 | 'compact_nullable_typehint' => true, 32 | 'concat_space' => ['spacing' => 'one'], 33 | 'declare_equal_normalize' => ['space' => 'none'], 34 | 'function_typehint_space' => true, 35 | 'new_with_braces' => true, 36 | 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], 37 | 'no_empty_statement' => true, 38 | 'no_leading_import_slash' => true, 39 | 'no_leading_namespace_whitespace' => true, 40 | 'no_whitespace_in_blank_line' => true, 41 | 'return_type_declaration' => ['space_before' => 'none'], 42 | 'single_trait_insert_per_statement' => true, 43 | ]) 44 | ->setFinder($finder); 45 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | push: 7 | branches: 8 | - main 9 | - 2.x 10 | pull_request: 11 | branches: 12 | - main 13 | - 2.x 14 | 15 | jobs: 16 | php-tests: 17 | runs-on: ${{ matrix.os }} 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: [ubuntu-latest] 23 | php: [8.1, 8.2, 8.3] 24 | laravel: ['10.*', '11.*', '12.*'] 25 | stability: [prefer-lowest, prefer-stable] 26 | include: 27 | - laravel: 10.* 28 | testbench: 8.* 29 | - laravel: 11.* 30 | testbench: 9.* 31 | - laravel: 12.* 32 | testbench: 10.* 33 | exclude: 34 | - laravel: 11.* 35 | php: 8.1 36 | - laravel: 12.* 37 | php: 8.1 38 | 39 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} 40 | 41 | steps: 42 | - name: Checkout code 43 | uses: actions/checkout@v2 44 | 45 | - name: Setup PHP 46 | uses: shivammathur/setup-php@v2 47 | with: 48 | php-version: ${{ matrix.php }} 49 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo 50 | coverage: pcov 51 | tools: composer:v2 52 | 53 | - name: Install dependencies 54 | run: | 55 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 56 | composer update --${{ matrix.stability }} --prefer-dist --no-interaction 57 | 58 | - name: Execute tests 59 | run: vendor/bin/pest --coverage --parallel 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "luigel/laravel-paymongo", 3 | "description": "A laravel wrapper for Paymongo API", 4 | "keywords": [ 5 | "luigel", 6 | "laravel-paymongo", 7 | "laravel", 8 | "paymongo", 9 | "gcash", 10 | "credit-cards", 11 | "debit-cards", 12 | "grabcar", 13 | "payment-gateway" 14 | ], 15 | "homepage": "https://github.com/luigel/laravel-paymongo", 16 | "license": "MIT", 17 | "type": "library", 18 | "authors": [ 19 | { 20 | "name": "Rigel Kent Carbonel", 21 | "email": "rigel20.kent@gmail.com", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": "^8.0", 27 | "guzzlehttp/guzzle": "^7.0", 28 | "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0" 29 | }, 30 | "require-dev": { 31 | "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", 32 | "pestphp/pest": "^2.34|^3.7" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "Luigel\\Paymongo\\": "src" 37 | } 38 | }, 39 | "autoload-dev": { 40 | "psr-4": { 41 | "Luigel\\Paymongo\\Tests\\": "tests" 42 | } 43 | }, 44 | "scripts": { 45 | "test": "vendor/bin/pest", 46 | "test-coverage": "vendor/bin/phpunit --coverage-html coverage" 47 | }, 48 | "config": { 49 | "sort-packages": true, 50 | "discard-changes": true, 51 | "allow-plugins": { 52 | "pestphp/pest-plugin": true 53 | } 54 | }, 55 | "extra": { 56 | "laravel": { 57 | "providers": [ 58 | "Luigel\\Paymongo\\PaymongoServiceProvider" 59 | ], 60 | "aliases": { 61 | "Paymongo": "Luigel\\Paymongo\\Facades\\Paymongo" 62 | } 63 | } 64 | }, 65 | "minimum-stability": "dev" 66 | } 67 | -------------------------------------------------------------------------------- /src/Middlewares/PaymongoValidateSignature.php: -------------------------------------------------------------------------------- 1 | headerPayload($request); 21 | 22 | if ($payload) { 23 | $signature = $this->signature($request, $payload['t'], $event); 24 | $key = config('paymongo.livemode') ? 'li' : 'te'; 25 | 26 | if ($signature == $payload[$key]) { 27 | return $next($request); 28 | } 29 | } 30 | throw new InvalidSignatureException(); 31 | } 32 | 33 | /** 34 | * Get header signature payload. 35 | */ 36 | public function headerPayload(Request $request): array 37 | { 38 | $payload = $request->header(app(Signer::class)->signatureHeaderName()); 39 | 40 | if ($payload === null) { 41 | return []; 42 | } 43 | 44 | return collect(explode(',', $payload)) 45 | ->mapWithKeys(function ($val) { 46 | $pair = explode('=', $val); 47 | 48 | return [$pair[0] => $pair[1]]; 49 | }) 50 | ->filter() 51 | ->all(); 52 | } 53 | 54 | /** 55 | * Get webhook signature. 56 | */ 57 | public function signature(Request $request, string|int $timestamp, ?string $event = null): string 58 | { 59 | return app(Signer::class)->calculateSignature( 60 | $timestamp, 61 | $request->getContent(), 62 | config('paymongo.webhook_signatures.'.$event, config('paymongo.webhook_signature')) 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Commands/WebhookAddCommand.php: -------------------------------------------------------------------------------- 1 | ask('Enter your webhook URL.'); 37 | 38 | $validate = $this->validate( 39 | ['url' => $url], 40 | ['url' => ['url', 'required']], 41 | 'Webhook was not created. See error messages below:' 42 | ); 43 | 44 | if ($validate === 1) { 45 | return 1; 46 | } 47 | 48 | $webhook = $this->createWebhook($url); 49 | $headers = ['id', 'livemode', 'secret_key', 'status', 'url']; 50 | $webhook = collect($webhook->getData())->only($headers)->toArray(); 51 | $webhook['livemode'] = $webhook['livemode'] ? 'YES' : 'NO'; 52 | 53 | $this->table($headers, [$webhook]); 54 | 55 | return 0; 56 | } 57 | 58 | protected function createWebhook(string $url): BaseModel 59 | { 60 | $this->comment('Creating webhook to Paymongo.'); 61 | 62 | $webhook = Paymongo::webhook()->create([ 63 | 'url' => $url, 64 | 'events' => [ 65 | Webhook::SOURCE_CHARGEABLE, 66 | ], 67 | ]); 68 | 69 | $this->line('Created webhook with an ID '.$webhook->id); 70 | 71 | return $webhook; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug Report" 3 | about: Report a general package issue. 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: luigel 7 | 8 | --- 9 | 10 | 22 | 23 | ### Prerequisites 24 | 25 | 28 | 29 | * [ ] Are you using Laravel 6 and above? 30 | * [ ] Able to reproduce the behaviour outside of your code, the problem is isolated to Laravel Paymongo. 31 | * [ ] Checked that your issue isn't already filed. 32 | * [ ] Checked if no PR was submitted that fixes this problem. 33 | * [ ] Filled in the entire issue template 34 | 35 | ### Versions 36 | 37 | 38 | 39 | * PHP version: 40 | * Laravel version: 41 | * Package version: 42 | 43 | ### Description 44 | 45 | 46 | 47 | ### Steps to Reproduce 48 | 49 | 50 | 51 | **Expected behavior:** 52 | 53 | 54 | 55 | **Actual behavior:** 56 | 57 | 58 | 59 | ### Additional Information 60 | 61 | Any additional information, configuration or data that might be necessary to reproduce the issue. 62 | -------------------------------------------------------------------------------- /docs/docs/Usage/customers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | slug: /customers 4 | id: customers 5 | --- 6 | 7 | # Customers 8 | 9 | ## Create Customer 10 | 11 | Creates a customer record that holds billing information and is useful for card vaulting purposes. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/customer-resource) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $customer = Paymongo::customer()->create([ 23 | 'first_name' => 'Juan', 24 | 'last_name' => 'Doe', 25 | 'phone' => '+639123456789', 26 | 'email' => 'customer@email.com', 27 | 'default_device' => 'phone' 28 | ]); 29 | ``` 30 | 31 | ## Get Customer 32 | 33 | Retrieve a customer by passing the customer id to the `find($id)` method. 34 | 35 | ### Sample 36 | 37 | ```php 38 | use Luigel\Paymongo\Facades\Paymongo; 39 | 40 | $customer = Paymongo::customer()->find('cus_b9ENKVqcHBfQQmv26uDYDCsD'); 41 | ``` 42 | 43 | ## Edit Customer 44 | 45 | Edit a customer by using the `find($id)` method and chaining the `->update()` method. Or by chaning `->update()` to your existing instance. 46 | 47 | ### Sample 48 | 49 | ```php 50 | use Luigel\Paymongo\Facades\Paymongo; 51 | 52 | $customer = Paymongo::customer() 53 | ->find('cus_b9ENKVqcHBfQQmv26uDYDCsD') 54 | ->update([ 55 | 'first_name' => 'Jane' 56 | ]); 57 | ``` 58 | 59 | ## Delete Customer 60 | 61 | Delete a customer by using the `find($id)` method and chaining the `->delete()` method. Or by chaning `->delete()` to your existing instance. 62 | 63 | ### Sample 64 | 65 | ```php 66 | use Luigel\Paymongo\Facades\Paymongo; 67 | 68 | $link = Paymongo::customer() 69 | ->find('cus_b9ENKVqcHBfQQmv26uDYDCsD') 70 | ->delete(); 71 | ``` 72 | 73 | ## Retrieve Customer's Payment Methods 74 | 75 | Retrieve the customer's payment methods by using the `find($id)` method and chaining the `->paymentMethods()` method. Or by chaning `->paymentMethods()` to your existing instance. 76 | 77 | ### Sample 78 | 79 | ```php 80 | use Luigel\Paymongo\Facades\Paymongo; 81 | 82 | $link = Paymongo::customer() 83 | ->find('cus_b9ENKVqcHBfQQmv26uDYDCsD') 84 | ->paymentMethods(); 85 | ``` -------------------------------------------------------------------------------- /docs/docs/Usage/checkouts.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 10 3 | slug: /checkout-sessions 4 | id: checkout-sessions 5 | --- 6 | 7 | # Checkouts 8 | 9 | ## Create Checkout 10 | 11 | Creates a checkout session. A checkout session is a customizable checkout page from Paymongo. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/checkout-session-resource) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $checkout = Paymongo::checkout()->create([ 23 | 'cancel_url' => 'https://paymongo.rigelkentcarbonel.com/', 24 | 'billing' => [ 25 | 'name' => 'Juan Doe', 26 | 'email' => 'juan@doe.com', 27 | 'phone' => '+639123456789', 28 | ], 29 | 'description' => 'My checkout session description', 30 | 'line_items' => [ 31 | [ 32 | 'amount' => 10000, 33 | 'currency' => 'PHP', 34 | 'description' => 'Something of a product.', 35 | 'images' => [ 36 | 'https://images.unsplash.com/photo-1613243555988-441166d4d6fd?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80' 37 | ], 38 | 'name' => 'A payment card', 39 | 'quantity' => 1 40 | ] 41 | ], 42 | 'payment_method_types' => [ 43 | 'atome', 44 | 'billease', 45 | 'card', 46 | 'dob', 47 | 'dob_ubp', 48 | 'gcash', 49 | 'grab_pay', 50 | 'paymaya' 51 | ], 52 | 'success_url' => 'https://paymongo.rigelkentcarbonel.com/', 53 | 'statement_descriptor' => 'Laravel Paymongo Library', 54 | 'metadata' => [ 55 | 'Key' => 'Value' 56 | ] 57 | ]); 58 | ``` 59 | 60 | ## Get Checkout 61 | 62 | Retrieve a checkout session by passing the id to the `find($id)` method. 63 | 64 | ### Sample 65 | 66 | ```php 67 | use Luigel\Paymongo\Facades\Paymongo; 68 | 69 | $checkout = Paymongo::checkout()->find('cs_CbFCTDfxvMFNjwjVi26Uzhtj'); 70 | ``` 71 | 72 | ## Expire Checkout 73 | 74 | Expire a checkout session by using the `find($id)` method and chaining the `->expire()` method. 75 | 76 | ### Sample 77 | 78 | ```php 79 | use Luigel\Paymongo\Facades\Paymongo; 80 | 81 | $checkout = Paymongo::checkout()->find('cs_CbFCTDfxvMFNjwjVi26Uzhtj')->expire(); 82 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paymongo for Laravel 2 | 3 | ![Run tests](https://github.com/luigel/laravel-paymongo/workflows/Run%20tests/badge.svg) 4 | [![Quality Score](https://img.shields.io/scrutinizer/g/luigel/laravel-paymongo.svg?style=flat-square)](https://scrutinizer-ci.com/g/luigel/laravel-paymongo) 5 | [![Latest Stable Version](https://poser.pugx.org/luigel/laravel-paymongo/v)](//packagist.org/packages/luigel/laravel-paymongo) 6 | [![Total Downloads](https://poser.pugx.org/luigel/laravel-paymongo/downloads)](//packagist.org/packages/luigel/laravel-paymongo) 7 | [![Monthly Downloads](https://poser.pugx.org/luigel/laravel-paymongo/d/monthly)](//packagist.org/packages/luigel/laravel-paymongo) 8 | [![Daily Downloads](https://poser.pugx.org/luigel/laravel-paymongo/d/daily)](//packagist.org/packages/luigel/laravel-paymongo) 9 | [![License](https://poser.pugx.org/luigel/laravel-paymongo/license)](//packagist.org/packages/luigel/laravel-paymongo) 10 | 11 | A PHP Library for [Paymongo](https://paymongo.com). 12 | 13 | This package is not affiliated with [Paymongo](https://paymongo.com). The package requires PHP 7.2+ 14 | 15 | ## Documentation 16 | 17 | https://paymongo.rigelkentcarbonel.com 18 | 19 | ### **Todo** 20 | 21 | - [x] Add unit test for the `BaseModel`. 22 | - [ ] Fix the magic method when accessing a nested data with `underscore` ("\_"). 23 | - [x] Add artisan commands for adding, enabling, and disabling webhooks. 24 | - [x] Fix the test case for the `PaymongoValidateSignature` middleware. 25 | - [x] Transfer from travis to github actions. 26 | - [x] Refactor test cases into Pest. 27 | 28 | ## Laravel Version Compatibility 29 | 30 | Laravel | Package 31 | :---------|:---------- 32 | 5.8.x | 1.x 33 | 6.x.x | 1.x 34 | 7.x.x | 1.x 35 | 8.x.x | 2.x 36 | 37 | ### Testing 38 | 39 | ```bash 40 | composer test 41 | ``` 42 | 43 | ### Changelog 44 | 45 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 46 | 47 | ## Contributing 48 | 49 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 50 | 51 | ### Security 52 | 53 | If you discover any security related issues, please email rigel20.kent@gmail.com instead of using the issue tracker. 54 | 55 | ## Credits 56 | 57 | - [Rigel Kent Carbonel](https://github.com/luigel) 58 | - [All Contributors](../../contributors) 59 | 60 | ## License 61 | 62 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 63 | 64 | Made with :heart: by Rigel Kent Carbonel 65 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const lightCodeTheme = require('prism-react-renderer/themes/github'); 2 | const darkCodeTheme = require('prism-react-renderer/themes/dracula'); 3 | 4 | // With JSDoc @type annotations, IDEs can provide config autocompletion 5 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 6 | (module.exports = { 7 | title: 'Laravel Paymongo', 8 | tagline: '', 9 | url: 'https://paymongo.rigelkentcarbonel.com', 10 | baseUrl: '/', 11 | onBrokenLinks: 'throw', 12 | onBrokenMarkdownLinks: 'warn', 13 | favicon: 'img/favicon.ico', 14 | organizationName: 'luigel', 15 | projectName: 'laravel-paymongo', 16 | noIndex: true, 17 | 18 | presets: [ 19 | [ 20 | '@docusaurus/preset-classic', 21 | /** @type {import('@docusaurus/preset-classic').Options} */ 22 | ({ 23 | docs: { 24 | routeBasePath: '/', 25 | sidebarPath: require.resolve('./sidebars.js'), 26 | editUrl: 'https://github.com/luigel/laravel-paymongo/edit/1.x/docs/', 27 | }, 28 | theme: { 29 | 30 | }, 31 | }), 32 | ], 33 | ], 34 | 35 | themeConfig: 36 | /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ 37 | ({ 38 | navbar: { 39 | title: 'Laravel Paymongo', 40 | logo: { 41 | alt: 'Paymongo Logo', 42 | src: 'img/paymongo-logo.png', 43 | }, 44 | items: [ 45 | { 46 | type: 'doc', 47 | docId: 'getting-started', 48 | position: 'left', 49 | label: 'Documentation', 50 | }, 51 | { 52 | href: 'https://github.com/luigel/laravel-paymongo', 53 | label: 'GitHub', 54 | position: 'right', 55 | }, 56 | ], 57 | }, 58 | footer: { 59 | style: 'dark', 60 | links: [ 61 | { 62 | title: 'Docs', 63 | items: [ 64 | { 65 | label: 'Documentation', 66 | to: '/', 67 | }, 68 | ], 69 | }, 70 | { 71 | title: 'Links', 72 | items: [ 73 | { 74 | label: 'Github', 75 | to: 'https://github.com/luigel', 76 | }, 77 | ], 78 | }, 79 | ], 80 | copyright: `Copyright © ${new Date().getFullYear()} Rigel Kent Carbonel. Built with Docusaurus.`, 81 | }, 82 | prism: { 83 | theme: lightCodeTheme, 84 | darkTheme: darkCodeTheme, 85 | additionalLanguages: ['php'] 86 | }, 87 | }) 88 | }); 89 | -------------------------------------------------------------------------------- /config/config.php: -------------------------------------------------------------------------------- 1 | env('PAYMONGO_LIVEMODE', false), 6 | 7 | /** 8 | * Public and Secret keys from Paymongo. You can get the keys here https://dashboard.paymongo.com/developers. 9 | */ 10 | 11 | /** 12 | * Public keys are meant to be used for any requests coming from the frontend, such as generating tokens or sources, 13 | * either using Javascript or through the mobile SDKs. 14 | * Public keys cannot be used to trigger payments or modify any part of the transaction flow. 15 | * They have the prefix pk_live_ for live mode and pk_test_ for test mode. 16 | */ 17 | 'public_key' => env('PAYMONGO_PUBLIC_KEY', null), 18 | 19 | /** 20 | * Secret keys, on the other hand, are for triggering or modifying payments. Never share your secret keys anywhere 21 | * that is publicly accessible: Github, client-side Javascript code, your website or even chat rooms. 22 | * The prefixes for the secret keys are sk_live_ for live mode and sk_test_ for test mode. 23 | */ 24 | 'secret_key' => env('PAYMONGO_SECRET_KEY', null), 25 | 26 | /** 27 | * Paymongo's team continuously adding more features and integrations to the API. 28 | * Currently, the API supports doing payments via debit and credit cards issued by Visa and Mastercard. 29 | */ 30 | 'version' => env('PAYMONGO_VERSION', '2019-08-05'), 31 | 32 | /* 33 | * This class is responsible for calculating the signature that will be added to 34 | * the headers of the webhook request. A webhook client can use the signature 35 | * to verify the request hasn't been tampered with. 36 | */ 37 | 'signer' => \Luigel\Paymongo\Signer\DefaultSigner::class, 38 | 39 | /** 40 | * Paymongo webhooks signature secret. 41 | */ 42 | 'webhook_signatures' => [ 43 | 'payment_paid' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_PAID', env('PAYMONGO_WEBHOOK_SIG')), 44 | 'payment_failed' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_FAILED', env('PAYMONGO_WEBHOOK_SIG')), 45 | 'source_chargeable' => env('PAYMONGO_WEBHOOK_SIG_SOURCE_CHARGABLE', env('PAYMONGO_WEBHOOK_SIG')), 46 | 'payment_refunded' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_REFUNDED', env('PAYMONGO_WEBHOOK_SIG')), 47 | 'payment_refund_updated' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_REFUND_UPDATED', env('PAYMONGO_WEBHOOK_SIG')), 48 | ], 49 | 50 | /** 51 | * Webhook signature configuration for backwards compatibility. 52 | */ 53 | 'webhook_signature' => env('PAYMONGO_WEBHOOK_SIG'), 54 | 55 | /* 56 | * This is the name of the header where the signature will be added. 57 | */ 58 | 'signature_header_name' => env('PAYMONGO_SIG_HEADER', 'paymongo-signature'), 59 | 60 | /** 61 | * This is the amount type to automatically convert the amount in your payload. 62 | * The default is Paymongo::AMOUNT_TYPE_FLOAT. 63 | * 64 | * Choices are: Paymongo::AMOUNT_TYPE_FLOAT, or Paymongo::AMOUNT_TYPE_INT 65 | */ 66 | 'amount_type' => \Luigel\Paymongo\Paymongo::AMOUNT_TYPE_FLOAT, 67 | ]; 68 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | Please read and understand the contribution guide before creating an issue or pull request. 6 | 7 | ## Etiquette 8 | 9 | This project is open source, and as such, the maintainers give their free time to build and maintain the source code 10 | held within. They make the code freely available in the hope that it will be of use to other developers. It would be 11 | extremely unfair for them to suffer abuse or anger for their hard work. 12 | 13 | Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the 14 | world that developers are civilized and selfless people. 15 | 16 | It's the duty of the maintainer to ensure that all submissions to the project are of sufficient 17 | quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. 18 | 19 | ## Viability 20 | 21 | When requesting or submitting new features, first consider whether it might be useful to others. Open 22 | source projects are used by many developers, who may have entirely different needs to your own. Think about 23 | whether or not your feature is likely to be used by other users of the project. 24 | 25 | ## Procedure 26 | 27 | Before filing an issue: 28 | 29 | - Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. 30 | - Check to make sure your feature suggestion isn't already present within the project. 31 | - Check the pull requests tab to ensure that the bug doesn't have a fix in progress. 32 | - Check the pull requests tab to ensure that the feature isn't already in progress. 33 | 34 | Before submitting a pull request: 35 | 36 | - Check the codebase to ensure that your feature doesn't already exist. 37 | - Check the pull requests to ensure that another person hasn't already submitted the feature or fix. 38 | 39 | ## Requirements 40 | 41 | If the project maintainer has any additional requirements, you will find them listed here. 42 | 43 | - **[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](https://pear.php.net/package/PHP_CodeSniffer). 44 | 45 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 46 | 47 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 48 | 49 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. 50 | 51 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 52 | 53 | - **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](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 54 | 55 | **Happy coding**! 56 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at rigel20.kent@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /src/Commands/WebhookToggleCommand.php: -------------------------------------------------------------------------------- 1 | setUp(); 57 | 58 | collect($this->webhookIds) 59 | ->each(function ($id) { 60 | $mode = $this->mode; 61 | $webhook = Paymongo::webhook()->find($id); 62 | 63 | if ($webhook->status !== $this->mode.'d') { 64 | $webhook->$mode(); 65 | $this->line(ucfirst($this->mode)."d webhook {$id}."); 66 | } else { 67 | $this->line("Webhook {$id} is already {$this->mode}d"); 68 | } 69 | }); 70 | } 71 | 72 | protected function setUp(): void 73 | { 74 | if ($this->option('enable')) { 75 | $this->mode = self::WEBHOOK_ENABLE; 76 | } 77 | 78 | if ($this->option('disable')) { 79 | $this->mode = self::WEBHOOK_DISABLE; 80 | } 81 | 82 | if ($this->hasArgument('webhookIds')) { 83 | $this->webhookIds = $this->argument('webhookIds'); 84 | } 85 | 86 | if (empty($this->webhookIds)) { 87 | $this->displayWebhooks(); 88 | 89 | $this->webhookIds = $this->ask('Enter the webhook id you want to enable or disable.'); 90 | 91 | $validate = $this->validate( 92 | ['webhook_id' => $this->webhookIds], 93 | ['webhook_id' => ['required']], 94 | 'See error messages below:' 95 | ); 96 | 97 | if ($validate === 1) { 98 | return; 99 | } 100 | 101 | $this->webhookIds = explode(',', $this->webhookIds); 102 | } 103 | 104 | if ($this->mode === null) { 105 | $this->mode = $this->choice('Enable or disable?', [ 106 | self::WEBHOOK_ENABLE, 107 | self::WEBHOOK_DISABLE, 108 | ], self::WEBHOOK_DISABLE); 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /docs/docs/Usage/payment-intents.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | slug: /payment-intents 4 | id: payment-intents 5 | --- 6 | 7 | # Payment Intents 8 | 9 | ## Create Payment Intent 10 | 11 | A payment intent is designed to handle a complex payment process. To compare payment intents with tokens, tokens have a straight forward credit card payment process where it does not check if 3DS is required to fulfill a payment while payment intent is designed to handle such process. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference/the-payment-intent-object) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $paymentIntent = Paymongo::paymentIntent()->create([ 23 | 'amount' => 100, 24 | 'payment_method_allowed' => [ 25 | 'card' 26 | ], 27 | 'payment_method_options' => [ 28 | 'card' => [ 29 | 'request_three_d_secure' => 'automatic' 30 | ] 31 | ], 32 | 'description' => 'This is a test payment intent', 33 | 'statement_descriptor' => 'LUIGEL STORE', 34 | 'currency' => "PHP", 35 | ]); 36 | ``` 37 | 38 | ## Cancel Payment Intent 39 | 40 | Cancels the payment intent. 41 | 42 | ### Sample 43 | 44 | ```php 45 | use Luigel\Paymongo\Facades\Paymongo; 46 | 47 | $paymentIntent = Paymongo::paymentIntent()->find('pi_hsJNpsRFU1LxgVbxW4YJHRs6'); 48 | $cancelledPaymentIntent = $paymentIntent->cancel(); 49 | ``` 50 | 51 | ## Attach Payment Intent 52 | 53 | Attach the payment intent. 54 | 55 | ### Sample 56 | 1. Simple attaching of payment method in payment intent. 57 | ```php 58 | use Luigel\Paymongo\Facades\Paymongo; 59 | 60 | $paymentIntent = Paymongo::paymentIntent()->find('pi_hsJNpsRFU1LxgVbxW4YJHRs6'); 61 | // Attached the payment method to the payment intent 62 | $successfulPayment = $paymentIntent->attach('pm_wr98R2gwWroVxfkcNVZBuXg2'); 63 | ``` 64 | 65 | 2. Attaching paymaya payment method in payment intent. 66 | ```php 67 | $paymentIntent = Paymongo::paymentIntent() 68 | ->create([ 69 | 'amount' => 100, 70 | 'payment_method_allowed' => [ 71 | 'paymaya', 'card' // <--- Make sure to add paymaya here. 72 | ], 73 | 'payment_method_options' => [ 74 | 'card' => [ 75 | 'request_three_d_secure' => 'automatic', 76 | ], 77 | ], 78 | 'description' => 'This is a test payment intent', 79 | 'statement_descriptor' => 'LUIGEL STORE', 80 | 'currency' => 'PHP', 81 | ]); 82 | 83 | $paymentMethod = Paymongo::paymentMethod() 84 | ->create([ 85 | 'type' => 'paymaya', // <--- and payment method type should be paymaya 86 | 'billing' => [ 87 | 'address' => [ 88 | 'line1' => 'Somewhere there', 89 | 'city' => 'Cebu City', 90 | 'state' => 'Cebu', 91 | 'country' => 'PH', 92 | 'postal_code' => '6000', 93 | ], 94 | 'name' => 'Rigel Kent Carbonel', 95 | 'email' => 'rigel20.kent@gmail.com', 96 | 'phone' => '0935454875545', 97 | ], 98 | ]); 99 | 100 | $attachedPaymentIntent = $paymentIntent->attach($paymentMethod->id, 'http://example.com/success'); // <--- And the second parameter should be the return_url. 101 | ``` 102 | 103 | ## Get Payment Intent 104 | 105 | You can retrieve a Payment Intent by providing a payment intent ID. The prefix for the id is `pi_` followed by a unique hash representing the payment. Just pass the payment id to `find($paymentIntentId)` method. 106 | 107 | ### Sample 108 | 109 | ```php 110 | use Luigel\Paymongo\Facades\Paymongo; 111 | 112 | $paymentIntent = Paymongo::paymentIntent()->find('pi_hsJNpsRFU1LxgVbxW4YJHRs6'); 113 | ``` 114 | -------------------------------------------------------------------------------- /src/Paymongo.php: -------------------------------------------------------------------------------- 1 | apiUrl = self::BASE_API.self::ENPDPOINT_SOURCES; 47 | $this->returnModel = Source::class; 48 | 49 | return $this; 50 | } 51 | 52 | /** 53 | * Webhook Module used to create, retrieve, enable, and disable Webhooks. 54 | */ 55 | public function webhook(): self 56 | { 57 | $this->apiUrl = self::BASE_API.self::ENDPOINT_WEBHOOKS; 58 | $this->returnModel = Webhook::class; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Payment Method Module used to create, retrieve Payment method informations. 65 | */ 66 | public function paymentMethod(): self 67 | { 68 | $this->apiUrl = self::BASE_API.self::ENDPOINT_PAYMENT_METHOD; 69 | $this->returnModel = PaymentMethod::class; 70 | 71 | return $this; 72 | } 73 | 74 | /** 75 | * Payment Intent Module used to create, retrieve, and attach payment method in payment intent. 76 | */ 77 | public function paymentIntent(): self 78 | { 79 | $this->apiUrl = self::BASE_API.self::ENDPOINT_PAYMENT_INTENT; 80 | $this->returnModel = PaymentIntent::class; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * Payment Module used to create, retrieve Payment information. 87 | */ 88 | public function payment(): self 89 | { 90 | $this->apiUrl = self::BASE_API.self::ENDPOINT_PAYMENT; 91 | $this->returnModel = Payment::class; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Token Module used to create and retrieve token. 98 | * 99 | * @deprecated 1.2.0 100 | */ 101 | public function token(): self 102 | { 103 | $this->apiUrl = self::BASE_API.self::ENDPOINT_TOKEN; 104 | $this->returnModel = Token::class; 105 | 106 | return $this; 107 | } 108 | 109 | public function refund(): self 110 | { 111 | $this->apiUrl = self::BASE_API.self::ENDPOINT_REFUND; 112 | $this->returnModel = Refund::class; 113 | 114 | return $this; 115 | } 116 | 117 | public function link(): self 118 | { 119 | $this->apiUrl = self::BASE_API.self::ENDPOINT_LINK; 120 | $this->returnModel = Link::class; 121 | 122 | return $this; 123 | } 124 | 125 | public function customer(): self 126 | { 127 | $this->apiUrl = self::BASE_API.self::ENDPOINT_CUSTOMER; 128 | $this->returnModel = Customer::class; 129 | 130 | return $this; 131 | } 132 | 133 | public function checkout(): self 134 | { 135 | $this->apiUrl = self::BASE_API.self::ENDPOINT_CHECKOUT; 136 | $this->returnModel = Checkout::class; 137 | 138 | return $this; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /.phpunit.result.cache: -------------------------------------------------------------------------------- 1 | {"version":1,"defects":{"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_attach_paymaya_payment_method_to_payment_intent":4,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_create_payment":4,"Luigel\\Paymongo\\Tests\\PaymentTest::it_cannot_create_payment_when_token_is_used_more_than_once":3,"Luigel\\Paymongo\\Tests\\PaymentTest::it_cannot_create_payment_when_token_is_not_valid":3,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_retrieve_a_payment":4,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_not_retrieve_a_payment_with_invalid_id":3,"Luigel\\Paymongo\\Tests\\PaymongoValidateSignatureTest::it_will_allow_valid_signature":4,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/BaseModelTest.php::it":4,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/PaymentIntentTest.php::it":3,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/PaymentTest.php::it":3,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/SourceTest.php::it":3,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/WebhookListCommandTest.php::it":4,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/AmounToIntegerTest.php::it":3,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/RefundTest.php::it":3},"times":{"Luigel\\Paymongo\\Tests\\AmounToIntegerTest::it_can_convert_without_decimal":0.042,"Luigel\\Paymongo\\Tests\\AmounToIntegerTest::it_can_convert_with_in_tenth_decimal":0.002,"Luigel\\Paymongo\\Tests\\AmounToIntegerTest::it_can_convert_with_in_hundredth_decimal":0.001,"Luigel\\Paymongo\\Tests\\BaseModelTest::it_can_set_data":0.003,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_create_payment_intent":1.034,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_cannot_create_payment_intent_when_data_is_invalid":0.809,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_cancel_payment_intent":1.694,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_attach_payment_method_to_payment_intent":2.908,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_cannot_attach_payment_intent_with_invalid_data":0.9,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_retrieve_payment_intent":1.774,"Luigel\\Paymongo\\Tests\\PaymentMethodTest::it_can_create_payment_method":0.966,"Luigel\\Paymongo\\Tests\\PaymentMethodTest::it_cannot_create_payment_method_with_invalid_data":0.834,"Luigel\\Paymongo\\Tests\\PaymentMethodTest::it_can_retrieve_payment_method":1.755,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_create_payment":2.189,"Luigel\\Paymongo\\Tests\\PaymentTest::it_cannot_create_payment_when_token_is_used_more_than_once":2.784,"Luigel\\Paymongo\\Tests\\PaymentTest::it_cannot_create_payment_when_token_is_not_valid":2.779,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_retrieve_a_payment":2.82,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_not_retrieve_a_payment_with_invalid_id":1.72,"Luigel\\Paymongo\\Tests\\PaymentTest::it_can_get_all_payments":1.811,"Luigel\\Paymongo\\Tests\\PaymongoValidateSignatureTest::it_will_not_allow_invalid_signature":0.009,"Luigel\\Paymongo\\Tests\\PaymongoValidateSignatureTest::it_will_allow_valid_signature":0.055,"Luigel\\Paymongo\\Tests\\SourceTest::it_can_create_a_gcash_source":0.875,"Luigel\\Paymongo\\Tests\\SourceTest::it_can_create_a_grab_pay_source":0.857,"Luigel\\Paymongo\\Tests\\TokenTest::it_can_create_token":0.912,"Luigel\\Paymongo\\Tests\\TokenTest::it_cannot_create_token_with_invalid_data":0.855,"Luigel\\Paymongo\\Tests\\TokenTest::it_can_retrieve_token":1.782,"Luigel\\Paymongo\\Tests\\UnauthorizedTest::it_expects_unauthorized_exception_with_invalid_api_keys":0.825,"Luigel\\Paymongo\\Tests\\WebhookTest::it_can_list_all_webhooks":0.849,"Luigel\\Paymongo\\Tests\\WebhookTest::it_can_retrieve_webhook":1.646,"Luigel\\Paymongo\\Tests\\PaymentIntentTest::it_can_attach_paymaya_payment_method_to_payment_intent":2.616,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/AmounToIntegerTest.php::it":0.047,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/BaseModelTest.php::it":0.061,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/PaymentIntentTest.php::it":1.063,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/PaymentTest.php::it":1.19,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/PaymongoValidateSignatureTest.php::it":0.061,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/SourceTest.php::it":0.883,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/TokenTest.php::it":0.815,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/UnauthorizedTest.php::it":1.045,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/WebhookTest.php::it":1.714,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/WebhookListCommandTest.php::it":1.957,"\/Users\/luigel\/Projects\/laravel-paymongo\/tests\/RefundTest.php::it":1.658,"\/Users\/luigel\/Projects\/php\/laravel-paymongo\/tests\/PaymentIntentTest.php::it":2.194}} -------------------------------------------------------------------------------- /docs/docs/Usage/webhooks.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | slug: /webhooks 4 | id: webhooks 5 | --- 6 | 7 | # Webhooks 8 | 9 | ## Create Webhook 10 | 11 | Creates a webhook. 12 | 13 | ### Payload 14 | 15 | Refer to [Paymongo documentation](https://developers.paymongo.com/reference#post_webhooks-1) for payload guidelines. 16 | 17 | ### Sample 18 | 19 | ```php 20 | use Luigel\Paymongo\Facades\Paymongo; 21 | 22 | $webhook = Paymongo::webhook()->create([ 23 | 'url' => 'http://your-domain/webhook/source-chargeable', 24 | 'events' => [ 25 | 'source.chargeable' 26 | ] 27 | ]); 28 | ``` 29 | 30 | ## List all Webhooks 31 | 32 | Returns all the webhooks you previously created, with the most recent webhooks returned first. 33 | 34 | ### Sample 35 | 36 | ```php 37 | use Luigel\Paymongo\Facades\Paymongo; 38 | 39 | $webhook = Paymongo::webhook()->all(); 40 | ``` 41 | 42 | ## Enable or Disable Webhooks 43 | 44 | Set the webhook enable or disable. 45 | 46 | ### Sample 47 | 48 | ```php 49 | use Luigel\Paymongo\Facades\Paymongo; 50 | // Enable webhook 51 | $webhook = Paymongo::webhook()->find('hook_9VrvpRkkYqK6twbhuvcVTtjM')->enable(); 52 | 53 | // Disable webhook 54 | $webhook = Paymongo::webhook()->find('hook_9VrvpRkkYqK6twbhuvcVTtjM')->disable(); 55 | ``` 56 | 57 | ## Update Webhook 58 | 59 | Updates a specific webhook 60 | 61 | ### Sample 62 | 63 | ```php 64 | use Luigel\Paymongo\Facades\Paymongo; 65 | 66 | $webhook = Paymongo::webhook()->find('hook_9VrvpRkkYqK6twbhuvcVTtjM')->update([ 67 | 'url' => 'https://update-domain.com/webhook' 68 | ]); 69 | ``` 70 | 71 | ## Webhook Middleware 72 | 73 | Laravel paymongo has a middleware for protecting your webhook, suggested by Paymongo. Check the link here. [**Securing a Webhook. Optional but highly recommended.**](https://developers.paymongo.com/docs/webhooks#3-securing-a-webhook-optional-but-highly-recommended) 74 | You can put your webhook in the `api.php` like so. 75 | 76 | ```php 77 | /** @var \Route $router */ 78 | $router->group( 79 | [ 80 | 'namespace' => 'Paymongo', 81 | 'as' => 'paymongo.', 82 | 'middleware' => 'paymongo.signature' // If you want to have only one signature key add this middleware in the group route where your webhook routes are defined. 83 | ], 84 | function () use ($router) { 85 | // This example is for different signature key for each webhook. 86 | $router->post( 87 | '/source-chargeable', 88 | 'PaymongoCallbackController@sourceChargeable' 89 | ) 90 | ->middleware('paymongo.signature:source_chargeable') 91 | ->name('source-chargeable'); 92 | 93 | $router->post( 94 | '/payment-paid', 95 | 'PaymongoCallbackController@paymentPaid' 96 | ) 97 | ->middleware('paymongo.signature:payment_paid') 98 | ->name('payment-paid'); 99 | 100 | $router->post( 101 | '/payment-failed', 102 | 'PaymongoCallbackController@paymentFailed' 103 | ) 104 | ->middleware('paymongo.signature:payment_failed') 105 | ->name('payment-failed'); 106 | 107 | $router->post( 108 | '/payment-refunded', 109 | 'PaymongoCallbackController@paymentRefunded' 110 | ) 111 | ->middleware('paymongo.signature:payment_refunded') 112 | ->name('payment-refunded'); 113 | 114 | $router->post( 115 | '/payment-refund-updated', 116 | 'PaymongoCallbackController@paymentRefundUpdated' 117 | ) 118 | ->middleware('paymongo.signature:payment_refund_updated') 119 | ->name('payment-refund-updated'); 120 | } 121 | ); 122 | 123 | # then add this to you .env file 124 | 125 | PAYMONGO_WEBHOOK_SIG_PAYMENT_PAID= 126 | PAYMONGO_WEBHOOK_SIG_PAYMENT_FAILED= 127 | PAYMONGO_WEBHOOK_SIG_SOURCE_CHARGABLE=. 128 | PAYMONGO_WEBHOOK_SIG_PAYMENT_REFUNDED=. 129 | PAYMONGO_WEBHOOK_SIG_PAYMENT_REFUND_UPDATED=. 130 | 131 | # you can get secret key when creating an webhook 132 | 133 | ``` 134 | 135 | ## Artisan Commands 136 | 137 | We can list, add, and toggle the `webhooks` using the artisan commands out of the box. 138 | 139 | - #### Adding webhook. 140 | ```bash 141 | php artisan paymongo:webhook 142 | ``` 143 | - #### List webhooks 144 | ```bash 145 | php artisan paymongo:list-webhooks 146 | ``` 147 | - #### Enable webhook with webhook id 148 | ```bash 149 | php artisan paymongo:toggle-webhook {webhook_id} --enable 150 | ``` 151 | - #### Disable webhook with webhook id 152 | ```bash 153 | php artisan paymongo:toggle-webhook {webhook_id} --disable 154 | ``` 155 | - #### Or you can just run paymongo:toggle-webhook and input needed data on runtime. 156 | ```bash 157 | php artisan paymongo:toggle-webhook 158 | ``` 159 | -------------------------------------------------------------------------------- /docs/docs/getting-started.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | slug: / 4 | id: getting-started 5 | --- 6 | 7 | # Getting Started 8 | 9 | ## Paymongo for Laravel 10 | 11 | ![Run tests](https://github.com/luigel/laravel-paymongo/workflows/Run%20tests/badge.svg) 12 | [![Quality Score](https://img.shields.io/scrutinizer/g/luigel/laravel-paymongo.svg?style=flat-square)](https://scrutinizer-ci.com/g/luigel/laravel-paymongo) 13 | [![Latest Stable Version](https://poser.pugx.org/luigel/laravel-paymongo/v)](//packagist.org/packages/luigel/laravel-paymongo) 14 | [![Total Downloads](https://poser.pugx.org/luigel/laravel-paymongo/downloads)](//packagist.org/packages/luigel/laravel-paymongo) 15 | [![Monthly Downloads](https://poser.pugx.org/luigel/laravel-paymongo/d/monthly)](//packagist.org/packages/luigel/laravel-paymongo) 16 | [![Daily Downloads](https://poser.pugx.org/luigel/laravel-paymongo/d/daily)](//packagist.org/packages/luigel/laravel-paymongo) 17 | [![License](https://poser.pugx.org/luigel/laravel-paymongo/license)](//packagist.org/packages/luigel/laravel-paymongo) 18 | 19 | A PHP Library for [Paymongo](https://paymongo.com). 20 | 21 | This package is not affiliated with [Paymongo](https://paymongo.com). The package requires PHP 7.2+ 22 | 23 | ## Installation 24 | 25 | You can install the package via composer: 26 | 27 | ```bash 28 | composer require luigel/laravel-paymongo 29 | ``` 30 | 31 | **Laravel 6 and up** uses Package Auto-Discovery, so doesn't require you to manually add the ServiceProvider. 32 | 33 | Put your `Secret Key` and `Public Key` and the `Webhook secret` in your `.env` file. 34 | 35 | ```env 36 | # Paymongo 37 | PAYMONGO_SECRET_KEY= 38 | PAYMONGO_PUBLIC_KEY= 39 | # This is the secret from the webhook you created. 40 | PAYMONGO_WEBHOOK_SIG= 41 | 42 | ``` 43 | ## Compatibility and Supported Versions 44 | 45 | Laravel-Paymongo supports Laravel 6.x and up. 46 | 47 | Laravel | Package 48 | :---------|:---------- 49 | 5.8.x | 1.x 50 | 6.x.x | 1.x 51 | 7.x.x | 1.x 52 | 8.x.x (PHP 7.4) | 1.x 53 | 8.x.x (PHP 8.0) | 2.x 54 | 55 | ## Configuring the package 56 | You can publish the config file by running: 57 | ```bash 58 | php artisan vendor:publish --provider="Luigel\Paymongo\PaymongoServiceProvider" --tag=config 59 | ``` 60 | 61 | This is the contents of the file that will be published at `config/paymongo.php`: 62 | ```php 63 | env('PAYMONGO_LIVEMODE', false), 68 | 69 | /** 70 | * Public and Secret keys from Paymongo. You can get the keys here https://dashboard.paymongo.com/developers. 71 | */ 72 | 73 | /** 74 | * Public keys are meant to be used for any requests coming from the frontend, such as generating tokens or sources, 75 | * either using Javascript or through the mobile SDKs. 76 | * Public keys cannot be used to trigger payments or modify any part of the transaction flow. 77 | * They have the prefix pk_live_ for live mode and pk_test_ for test mode. 78 | */ 79 | 'public_key' => env('PAYMONGO_PUBLIC_KEY', null), 80 | 81 | /** 82 | * Secret keys, on the other hand, are for triggering or modifying payments. Never share your secret keys anywhere 83 | * that is publicly accessible: Github, client-side Javascript code, your website or even chat rooms. 84 | * The prefixes for the secret keys are sk_live_ for live mode and sk_test_ for test mode. 85 | */ 86 | 'secret_key' => env('PAYMONGO_SECRET_KEY', null), 87 | 88 | /** 89 | * Paymongo's team continuously adding more features and integrations to the API. 90 | * Currently, the API supports doing payments via debit and credit cards issued by Visa and Mastercard. 91 | */ 92 | 'version' => env('PAYMONGO_VERSION', '2019-08-05'), 93 | 94 | /* 95 | * This class is responsible for calculating the signature that will be added to 96 | * the headers of the webhook request. A webhook client can use the signature 97 | * to verify the request hasn't been tampered with. 98 | */ 99 | 'signer' => \Luigel\Paymongo\Signer\DefaultSigner::class, 100 | 101 | /** 102 | * Paymongo webhooks signature secret. 103 | */ 104 | 'webhook_signatures' => [ 105 | 'payment_paid' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_PAID', env('PAYMONGO_WEBHOOK_SIG')), 106 | 'payment_failed' => env('PAYMONGO_WEBHOOK_SIG_PAYMENT_FAILED', env('PAYMONGO_WEBHOOK_SIG')), 107 | 'source_chargeable' => env('PAYMONGO_WEBHOOK_SIG_SOURCE_CHARGABLE', env('PAYMONGO_WEBHOOK_SIG')), 108 | ], 109 | 110 | /** 111 | * Webhook signature configuration for backwards compatibility. 112 | */ 113 | 'webhook_signature' => env('PAYMONGO_WEBHOOK_SIG'), 114 | 115 | /* 116 | * This is the name of the header where the signature will be added. 117 | */ 118 | 'signature_header_name' => env('PAYMONGO_SIG_HEADER', 'paymongo-signature'), 119 | 120 | /** 121 | * This is the amount type to automatically convert the amount in your payload. 122 | * The default is Paymongo::AMOUNT_TYPE_FLOAT. 123 | * 124 | * Choices are: Paymongo::AMOUNT_TYPE_FLOAT, or Paymongo::AMOUNT_TYPE_INT 125 | */ 126 | 'amount_type' => \Luigel\Paymongo\Paymongo::AMOUNT_TYPE_FLOAT, 127 | ]; 128 | 129 | ``` 130 | -------------------------------------------------------------------------------- /src/Models/BaseModel.php: -------------------------------------------------------------------------------- 1 | setSingleData($data); 26 | } 27 | 28 | $collection = collect(); 29 | 30 | foreach ($data as $item) { 31 | $collection->push($this->setSingleData($item)); 32 | } 33 | 34 | return $collection; 35 | } 36 | 37 | /** 38 | * Set the single data to the attributes. 39 | */ 40 | public function setSingleData(array $data): self 41 | { 42 | $model = new static(); 43 | 44 | foreach ($data as $key => $item) { 45 | $model->setAttributes($key, $item); 46 | } 47 | 48 | return $model; 49 | } 50 | 51 | /** 52 | * Get all the attributes. 53 | */ 54 | public function getAttributes(): array 55 | { 56 | return $this->attributes; 57 | } 58 | 59 | /** 60 | * Alias for getAttributes. 61 | */ 62 | public function getData(): array 63 | { 64 | return $this->getAttributes(); 65 | } 66 | 67 | /** 68 | * Set the attributes by key and value. 69 | */ 70 | public function setAttributes(string $key, mixed $item): void 71 | { 72 | if (is_array($item)) { 73 | foreach ($item as $itemKey => $element) { 74 | $this->$itemKey = $element; 75 | } 76 | 77 | return; 78 | } 79 | $this->$key = $item; 80 | } 81 | 82 | public function __set(string $key, mixed $value): void 83 | { 84 | if (array_key_exists($key, $this->attributes)) { 85 | $this->attributes[$this->keyFormatFromClass($key)] = $value; 86 | } else { 87 | $this->attributes[$key] = $value; 88 | } 89 | } 90 | 91 | public function __get(string $key): mixed 92 | { 93 | // This will ensure the amount is converted to float. 94 | if ($amount = $this->ensureFloatAmount($key)) { 95 | return $amount; 96 | } 97 | 98 | return $this->attributes[$key]; 99 | } 100 | 101 | /** 102 | * The magic function that guesses the attribute. 103 | */ 104 | public function __call(string $name, mixed $arguments): mixed 105 | { 106 | return $this->guessAttributeFromMethodName($name); 107 | } 108 | 109 | /** 110 | * Guess the attribute from the method name. 111 | */ 112 | protected function guessAttributeFromMethodName(string $method): mixed 113 | { 114 | $this->method = $method; 115 | 116 | $key = Str::snake(Str::after($method, 'get')); 117 | 118 | if (array_key_exists($key, $this->attributes)) { 119 | if ($amount = $this->ensureFloatAmount($key)) { 120 | return $amount; 121 | } 122 | 123 | return $this->attributes[$key]; 124 | } 125 | 126 | $keys = explode('_', $key); 127 | 128 | $currentAttribute = null; 129 | foreach ($keys as $key) { 130 | $currentAttribute = $this->getGuessedData($key, $currentAttribute); 131 | } 132 | 133 | return $currentAttribute; 134 | } 135 | 136 | /** 137 | * Get the guessed data. 138 | * 139 | * @throws \Luigel\Paymongo\Exceptions\MethodNotFoundException 140 | */ 141 | protected function getGuessedData(string $key, mixed $currentAttribute): mixed 142 | { 143 | try { 144 | if ($currentAttribute === null && ! is_array($this->attributes[$key])) { 145 | if ($amount = $this->ensureFloatAmount($key)) { 146 | return $amount; 147 | } 148 | 149 | return $this->attributes[$key]; 150 | } 151 | } catch (Exception $e) { 152 | $this->throwMethodNotFoundException(); 153 | } 154 | 155 | if (array_key_exists($key, $this->attributes)) { 156 | return $this->attributes[$key]; 157 | } 158 | 159 | if (array_key_exists($key, $currentAttribute)) { 160 | $currentAttribute = $currentAttribute[$key]; 161 | } else { 162 | $this->throwMethodNotFoundException(); 163 | } 164 | 165 | return $currentAttribute; 166 | } 167 | 168 | /** 169 | * Throws the method not found exception. 170 | * 171 | * @throws \Luigel\Paymongo\Exceptions\MethodNotFoundException 172 | */ 173 | protected function throwMethodNotFoundException(): void 174 | { 175 | throw new MethodNotFoundException("Method [{$this->method}] not found in ".get_class($this)); 176 | } 177 | 178 | /** 179 | * Get the class name in snake format. 180 | */ 181 | protected function keyFormatFromClass(string $key): string 182 | { 183 | return Str::snake($this->getModel()).'_'.$key; 184 | } 185 | 186 | /** 187 | * Get the model. 188 | */ 189 | protected function getModel(): string 190 | { 191 | return Str::afterLast(get_class($this), '\\'); 192 | } 193 | 194 | public function ensureFloatAmount(string $key): ?float 195 | { 196 | if ($key === 'amount') { 197 | return floatval($this->attributes[$key] / 100); 198 | } 199 | 200 | return null; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/Traits/Request.php: -------------------------------------------------------------------------------- 1 | method = 'POST'; 33 | $this->payload = $this->convertPayloadAmountsToInteger($payload); 34 | $this->formRequestData(); 35 | 36 | $this->setGuzzleOptions([ 37 | 'headers' => [ 38 | 'Accept' => 'application/json', 39 | 'Content-type' => 'application/json', 40 | ], 41 | 'auth' => [config('paymongo.secret_key'), ''], 42 | 'json' => $this->data, 43 | ]); 44 | 45 | return $this->request(); 46 | } 47 | 48 | /** 49 | * Request to retrieve a resource in API. 50 | */ 51 | public function find(string $payload): BaseModel 52 | { 53 | $this->method = 'GET'; 54 | $this->apiUrl = $this->apiUrl.$payload; 55 | 56 | $this->setGuzzleOptions([ 57 | 'headers' => [ 58 | 'Accept' => 'application/json', 59 | 'Content-type' => 'application/json', 60 | ], 61 | 'auth' => [config('paymongo.secret_key'), ''], 62 | ]); 63 | 64 | return $this->request(); 65 | } 66 | 67 | /** 68 | * Request a get all to API. 69 | */ 70 | public function all(): Collection 71 | { 72 | $this->method = 'GET'; 73 | 74 | $this->setGuzzleOptions([ 75 | 'headers' => [ 76 | 'Accept' => 'application/json', 77 | 'Content-type' => 'application/json', 78 | ], 79 | 'auth' => [config('paymongo.secret_key'), ''], 80 | ]); 81 | 82 | return $this->request(); 83 | } 84 | 85 | /** 86 | * Updates the webhook. 87 | */ 88 | public function update(Webhook $webhook, array $payload): BaseModel 89 | { 90 | $this->method = 'PUT'; 91 | $this->payload = $this->convertPayloadAmountsToInteger($payload); 92 | $this->apiUrl = $this->apiUrl.$webhook->id; 93 | 94 | $this->formRequestData(); 95 | $this->setGuzzleOptions([ 96 | 'headers' => [ 97 | 'Accept' => 'application/json', 98 | ], 99 | 'auth' => [config('paymongo.secret_key'), ''], 100 | 'json' => $this->data, 101 | ]); 102 | 103 | return $this->request(); 104 | } 105 | 106 | /** 107 | * Cancels the payment intent. 108 | */ 109 | public function cancel(PaymentIntent $intent): BaseModel 110 | { 111 | $this->method = 'POST'; 112 | $this->apiUrl = $this->apiUrl.$intent->id.'/cancel'; 113 | 114 | $this->setGuzzleOptions([ 115 | 'headers' => [ 116 | 'Accept' => 'application/json', 117 | ], 118 | 'auth' => [config('paymongo.secret_key'), ''], 119 | ]); 120 | 121 | return $this->request(); 122 | } 123 | 124 | /** 125 | * Attach the payment method in the payment intent. 126 | */ 127 | public function attach(PaymentIntent $intent, string $paymentMethodId, ?string $returnUrl = null): BaseModel 128 | { 129 | $this->method = 'POST'; 130 | $this->apiUrl = $this->apiUrl.$intent->id.'/attach'; 131 | $this->payload = ['payment_method' => $paymentMethodId]; 132 | 133 | if ($returnUrl) { 134 | $this->payload = array_merge($this->payload, ['return_url' => $returnUrl]); 135 | } 136 | 137 | $this->formRequestData(); 138 | $this->setGuzzleOptions([ 139 | 'headers' => [ 140 | 'Accept' => 'application/json', 141 | ], 142 | 'json' => $this->data, 143 | 'auth' => [config('paymongo.secret_key'), ''], 144 | ]); 145 | 146 | return $this->request(); 147 | } 148 | 149 | /** 150 | * Archives the link. 151 | */ 152 | public function archive(Link $link) 153 | { 154 | $this->method = 'POST'; 155 | $this->apiUrl = $this->apiUrl.$link->id.'/archive'; 156 | 157 | $this->setGuzzleOptions([ 158 | 'headers' => [ 159 | 'Accept' => 'application/json', 160 | ], 161 | 'auth' => [config('paymongo.secret_key'), ''], 162 | ]); 163 | 164 | return $this->request(); 165 | } 166 | 167 | /** 168 | * Unarchives the link. 169 | */ 170 | public function unarchive(Link $link) 171 | { 172 | $this->method = 'POST'; 173 | $this->apiUrl = $this->apiUrl.$link->id.'/unarchive'; 174 | 175 | $this->setGuzzleOptions([ 176 | 'headers' => [ 177 | 'Accept' => 'application/json', 178 | ], 179 | 'auth' => [config('paymongo.secret_key'), ''], 180 | ]); 181 | 182 | return $this->request(); 183 | } 184 | 185 | /** 186 | * Update the customer information. 187 | */ 188 | public function updateCustomer(Customer $customer, array $payload) 189 | { 190 | $this->method = 'PATCH'; 191 | $this->apiUrl = $this->apiUrl.$customer->id; 192 | $this->payload = $payload; 193 | 194 | $this->formRequestData(); 195 | $this->setGuzzleOptions([ 196 | 'headers' => [ 197 | 'Accept' => 'application/json', 198 | ], 199 | 'json' => $this->data, 200 | 'auth' => [config('paymongo.secret_key'), ''], 201 | ]); 202 | 203 | return $this->request(); 204 | } 205 | 206 | /** 207 | * Delete the customer. 208 | */ 209 | public function deleteCustomer(Customer $customer) 210 | { 211 | $this->method = 'DELETE'; 212 | $this->apiUrl = $this->apiUrl.$customer->id; 213 | 214 | $this->setGuzzleOptions([ 215 | 'headers' => [ 216 | 'Accept' => 'application/json', 217 | ], 218 | 'auth' => [config('paymongo.secret_key'), ''], 219 | ]); 220 | 221 | return $this->request(); 222 | } 223 | 224 | /** 225 | * Get Customer's Payment Methods. 226 | */ 227 | public function getPaymentMethods(Customer $customer) 228 | { 229 | $this->method = 'GET'; 230 | $this->apiUrl = $this->apiUrl.$customer->id.'/payment_methods'; 231 | 232 | $this->setGuzzleOptions([ 233 | 'headers' => [ 234 | 'Accept' => 'application/json', 235 | ], 236 | 'auth' => [config('paymongo.secret_key'), ''], 237 | ]); 238 | 239 | return $this->request(); 240 | } 241 | 242 | public function expireCheckout(Checkout $checkout) 243 | { 244 | $this->method = 'POST'; 245 | $this->apiUrl = $this->apiUrl.$checkout->id.'/expire'; 246 | 247 | $this->setGuzzleOptions([ 248 | 'headers' => [ 249 | 'Accept' => 'application/json', 250 | ], 251 | 'auth' => [config('paymongo.secret_key'), ''], 252 | ]); 253 | 254 | return $this->request(); 255 | } 256 | 257 | /** 258 | * Send request to API. 259 | * 260 | * @throws \Luigel\Paymongo\Exceptions\BadRequestException 261 | * @throws \Luigel\Paymongo\Exceptions\UnauthorizedException 262 | * @throws \Luigel\Paymongo\Exceptions\PaymentErrorException 263 | * @throws \Luigel\Paymongo\Exceptions\NotFoundException 264 | * @throws \Exception 265 | */ 266 | protected function request(): BaseModel|Collection 267 | { 268 | $client = new Client(); 269 | 270 | try { 271 | $response = $client->request($this->method, $this->apiUrl, $this->guzzleOptions); 272 | 273 | $array = $this->parseToArray((string) $response->getBody()); 274 | 275 | return $this->setReturnModel($array); 276 | } catch (ClientException $e) { 277 | $response = $e->getResponse()->getBody()->getContents(); 278 | if ($e->getCode() === 400) { 279 | throw new BadRequestException($response, $e->getCode()); 280 | } elseif ($e->getCode() === 401) { 281 | throw new UnauthorizedException($response, $e->getCode()); 282 | } elseif ($e->getCode() === 402) { 283 | throw new PaymentErrorException($response, $e->getCode()); 284 | } elseif ($e->getCode() === 404) { 285 | throw new NotFoundException($response, $e->getCode()); 286 | } 287 | 288 | throw new Exception($response, $e->getCode()); 289 | } 290 | } 291 | 292 | /** 293 | * Sets the data to add data wrapper of the payload. 294 | * 295 | * @return void 296 | */ 297 | protected function formRequestData() 298 | { 299 | $this->data = [ 300 | 'data' => [ 301 | 'attributes' => $this->payload, 302 | ], 303 | ]; 304 | } 305 | 306 | /** 307 | * Parses json to array. 308 | * 309 | * @param string $json 310 | * @return array 311 | */ 312 | protected function parseToArray($json) 313 | { 314 | return json_decode($json, true); 315 | } 316 | 317 | /** 318 | * Set the return model with the data. 319 | * 320 | * @param array $array 321 | * @return mixed 322 | */ 323 | protected function setReturnModel($array) 324 | { 325 | return (new $this->returnModel)->setData($array['data']); 326 | } 327 | 328 | /** 329 | * Set the options. 330 | * 331 | * @param array $guzzleOptions 332 | * @return $this 333 | */ 334 | protected function setGuzzleOptions($guzzleOptions) 335 | { 336 | $this->guzzleOptions = $guzzleOptions; 337 | 338 | return $this; 339 | } 340 | 341 | /** 342 | * Converts the Payload Amount to Integer. 343 | * 344 | * @param array $payload 345 | * @return array 346 | * 347 | * @throws \Luigel\Paymongo\Exceptions\AmountTypeNotSupportedException 348 | */ 349 | protected function convertPayloadAmountsToInteger($payload) 350 | { 351 | if (isset($payload['amount'])) { 352 | $payload['amount'] = match ($amountType = config('paymongo.amount_type', 'float')) { 353 | 'float' => (int) number_format($payload['amount'] * 100, 0, '', ''), 354 | 'int' => (int) $payload['amount'], 355 | default => throw new AmountTypeNotSupportedException("The amount_type [$amountType] used is not supported."), 356 | }; 357 | } 358 | 359 | return $payload; 360 | } 361 | } 362 | --------------------------------------------------------------------------------