├── .github ├── FUNDING.yml ├── dependabot.yml ├── stale.yml └── workflows │ ├── release.yml │ ├── static.yml │ └── tests.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── cliff.toml ├── phpstan.neon.dist └── src ├── Gateway.php └── Message ├── AbstractRequest.php ├── AbstractResponse.php ├── PurchaseCompleteRequest.php ├── PurchaseCompleteResponse.php ├── PurchaseRequest.php └── PurchaseResponse.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: nekofar 4 | custom: https://unstoppabledomains.com/d/nekofar.crypto 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: "github-actions" 5 | # Files stored in repository root 6 | directory: "/" 7 | # Check for updates every weekday 8 | schedule: 9 | interval: "daily" 10 | # Add assignees 11 | assignees: 12 | - "nekofar" 13 | # Include a list of updated dependencies 14 | commit-message: 15 | prefix: "ci" 16 | include: "scope" 17 | # Specify labels for pull requests 18 | labels: 19 | - "dependencies" 20 | # Allow up to 10 open pull requests for dependencies 21 | open-pull-requests-limit: 20 22 | # Add reviewers 23 | reviewers: 24 | - "nekofar" 25 | # Raise pull requests against the `develop` branch 26 | target-branch: "develop" 27 | 28 | # Maintain dependencies for Composer 29 | - package-ecosystem: "composer" 30 | # Files stored in repository root 31 | directory: "/" 32 | # Check for updates every weekday 33 | schedule: 34 | interval: "daily" 35 | # Add assignees 36 | assignees: 37 | - "nekofar" 38 | # Include a list of updated dependencies 39 | commit-message: 40 | prefix: "chore" 41 | include: "scope" 42 | # Specify labels for pull requests 43 | labels: 44 | - "dependencies" 45 | # Allow up to 10 open pull requests for dependencies 46 | open-pull-requests-limit: 20 47 | # Add reviewers 48 | reviewers: 49 | - "nekofar" 50 | # Raise pull requests against the `develop` branch 51 | target-branch: "develop" 52 | 53 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | changelog: 10 | name: Generate changelog 11 | runs-on: ubuntu-latest 12 | outputs: 13 | release_body: ${{ steps.release.outputs.RELEASE_BODY }} 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3.5.2 17 | with: 18 | fetch-depth: 0 19 | 20 | - name: Generate a changelog 21 | uses: orhun/git-cliff-action@v2.0.6 22 | id: git-cliff 23 | with: 24 | config: cliff.toml 25 | args: -vv --latest --strip all 26 | env: 27 | OUTPUT: CHANGES.md 28 | 29 | - name: Set the release body 30 | id: release 31 | shell: bash 32 | run: | 33 | r=$(cat ${{ steps.git-cliff.outputs.changelog }}) 34 | r="$(printf "$r" | tail -n +3)" 35 | r="${r//'%'/'%25'}" # Multiline escape sequences for % 36 | r="${r//$'\n'/'%0A'}" # Multiline escape sequences for '\n' 37 | r="${r//$'\r'/'%0D'}" # Multiline escape sequences for '\r' 38 | echo "::set-output name=RELEASE_BODY::$r" 39 | 40 | # use release body in another job 41 | publish: 42 | name: Publish on the GitHub 43 | needs: changelog 44 | runs-on: ubuntu-latest 45 | steps: 46 | - name: Create the GitHub release 47 | uses: softprops/action-gh-release@v0.1.15 48 | with: 49 | body: ${{ needs.changelog.outputs.release_body }} 50 | prerelease: ${{ contains(github.ref, '-rc') || contains(github.ref, '-beta') || contains(github.ref, '-alpha') }} 51 | -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | name: Static Analysis 2 | 3 | on: ['push', 'pull_request'] 4 | 5 | jobs: 6 | phpcs: 7 | runs-on: ubuntu-latest 8 | 9 | name: Code Sniff 10 | 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v3.5.2 14 | 15 | - name: Setup PHP 16 | uses: shivammathur/setup-php@2.25.1 17 | with: 18 | php-version: 8.1 19 | tools: composer:v2 20 | coverage: none 21 | 22 | - name: Get Composer Cache Directory 23 | id: composer-cache 24 | run: | 25 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 26 | - uses: actions/cache@v3.3.1 27 | with: 28 | path: ${{ steps.composer-cache.outputs.dir }} 29 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 30 | restore-keys: | 31 | ${{ runner.os }}-composer- 32 | 33 | - name: Install Dependencies 34 | run: composer update --no-interaction --no-progress --ansi 35 | 36 | - name: Run the PHP CodeSniffer 37 | run: vendor/bin/phpcs 38 | 39 | phpstan: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | dependency-version: [prefer-lowest, prefer-stable] 44 | 45 | name: PHPStan ${{ matrix.dependency-version }} 46 | 47 | steps: 48 | - name: Checkout 49 | uses: actions/checkout@v3.5.2 50 | 51 | - name: Setup PHP 52 | uses: shivammathur/setup-php@2.25.1 53 | with: 54 | php-version: 7.4 55 | tools: composer:v2 56 | coverage: none 57 | 58 | - name: Install Dependencies 59 | run: composer update --prefer-stable --no-interaction --no-progress --ansi 60 | 61 | - name: Run PHPStan 62 | run: vendor/bin/phpstan analyse --no-progress --ansi 63 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ 'push', 'pull_request' ] 4 | 5 | jobs: 6 | ci: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ ubuntu-latest, macos-latest, windows-latest ] 11 | php: [ '8.1', '8.2' ] 12 | dependency-version: [ prefer-lowest, prefer-stable ] 13 | 14 | name: PHP ${{ matrix.php }} - ${{ matrix.os }} - ${{ matrix.dependency-version }} 15 | 16 | steps: 17 | - name: Checkout repository 18 | uses: actions/checkout@v3.5.2 19 | 20 | - name: Setup PHP environment 21 | uses: shivammathur/setup-php@2.25.1 22 | id: setup-php 23 | with: 24 | php-version: ${{ matrix.php }} 25 | tools: composer:v2 26 | coverage: pcov 27 | 28 | - name: Get Composer Cache Directory 29 | id: composer-cache-common 30 | if: "${{ runner.os != 'Windows' }}" 31 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 32 | - name: Get Composer Cache Directory 33 | id: composer-cache-windows 34 | if: "${{ runner.os == 'Windows' }}" 35 | run: echo "dir=$(composer config cache-files-dir)" >> $env:GITHUB_OUTPUT 36 | - uses: actions/cache@v3.3.1 37 | with: 38 | path: ${{ steps.composer-cache-common.outputs.dir }}${{ steps.composer-cache-windows.outputs.dir }} 39 | key: ${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer-${{ hashFiles('**/composer.lock') }} 40 | restore-keys: | 41 | ${{ runner.os }}-${{ steps.setup-php.outputs.php-version }}-composer- 42 | 43 | - name: Setup problem matches 44 | run: | 45 | echo "::add-matcher::${{ runner.tool_cache }}/php.json" 46 | echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" 47 | 48 | - name: Install PHP dependencies 49 | run: composer update --${{ matrix.dependency-version }} --no-interaction --no-progress --ansi 50 | 51 | - name: Run unit tests by phpunit 52 | run: ./vendor/bin/phpunit --testdox --coverage-clover coverage.xml 53 | 54 | - name: Collect test coverage 55 | uses: codecov/codecov-action@v3.1.4 56 | with: 57 | token: ${{ secrets.CODECOV_TOKEN }} 58 | files: ./coverage.xml 59 | 60 | concurrency: 61 | group: ${{ github.workflow }}-${{ github.ref }} 62 | cancel-in-progress: true 63 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [2.0.1] - 2023-05-18 6 | 7 | ### Miscellaneous Tasks 8 | 9 | - Bump dependencies lowest version 10 | - Remove deprecated `phpcs` rules 11 | 12 | ## [2.0.0] - 2023-05-18 13 | 14 | ### Continuous Integrations 15 | 16 | - Drop support for php version `<8.1` on workflows 17 | 18 | ### Miscellaneous Tasks 19 | 20 | - Normalize composer configurations 21 | - [**breaking**] Drop support for php version `<8.1` 22 | 23 | ## [1.3.11] - 2023-05-18 24 | 25 | ### Miscellaneous Tasks 26 | 27 | - Update `composer` config state 28 | 29 | ## [1.3.10] - 2023-05-18 30 | 31 | ### Continuous Integrations 32 | 33 | - Bump codecov/codecov-action from 3.1.1 to 3.1.2 34 | - Bump actions/checkout from 3.5.0 to 3.5.2 35 | - Bump shivammathur/setup-php from 2.24.0 to 2.25.0 36 | - Bump shivammathur/setup-php from 2.25.0 to 2.25.1 37 | - Bump codecov/codecov-action from 3.1.2 to 3.1.3 38 | - Bump orhun/git-cliff-action from 2.0.5 to 2.0.6 39 | - Change `open-pull-requests-limit` from 10 to 20 40 | - Bump codecov/codecov-action from 3.1.3 to 3.1.4 41 | 42 | ## [1.3.9] - 2023-04-04 43 | 44 | ### Documentation 45 | 46 | - Update twitter badge due to depreciation 47 | 48 | ### Continuous Integrations 49 | 50 | - Bump actions/cache from 3.2.2 to 3.2.3 51 | - Bump orhun/git-cliff-action from 2.0.1 to 2.0.3 52 | - Bump shivammathur/setup-php from 2.23.0 to 2.24.0 53 | - Bump orhun/git-cliff-action from 2.0.3 to 2.0.5 54 | - Bump actions/cache from 3.2.3 to 3.3.1 55 | - Bump actions/checkout from 3.2.0 to 3.5.0 56 | 57 | ## [1.3.8] - 2023-01-05 58 | 59 | ### Continuous Integrations 60 | 61 | - Add a new `release` workflow to create releases 62 | 63 | ## [1.3.7] - 2023-01-05 64 | 65 | ### Documentation 66 | 67 | - Update workflow badge url over readme file 68 | 69 | ### Continuous Integrations 70 | 71 | - Setup stale bot to closes abandoned issues 72 | - Bump shivammathur/setup-php from 2.22.0 to 2.23.0 73 | - Bump actions/checkout from 3.2.0 to 3.3.0 74 | - Bump actions/cache from 3.0.11 to 3.2.2 75 | - Add `php` version ^8.1 and ^8.2 to `tests` workflow 76 | - Solve issues regarding composer cache on windows on `tests` workflow 77 | 78 | ### Miscellaneous Tasks 79 | 80 | - Migrate from semver to git cliff for changelogs 81 | 82 | ## [1.3.6] - 2022-12-14 83 | 84 | ### Continuous Integrations 85 | 86 | - Bump shivammathur/setup-php from 2.21.0 to 2.21.1 87 | - Bump shivammathur/setup-php from 2.21.1 to 2.21.2 88 | - Bump actions/cache from 3.0.5 to 3.0.7 89 | - Bump actions/checkout from 3.0.2 to 3.2.0 90 | - Bump actions/cache from 3.0.7 to 3.0.11 91 | - Bump shivammathur/setup-php from 2.21.2 to 2.22.0 92 | - Replace deprecated `set-output` command 93 | - Bump codecov/codecov-action from 3.1.0 to 3.1.1 94 | 95 | ## [1.3.5] - 2022-07-20 96 | 97 | ### Continuous Integrations 98 | 99 | - Bump actions/cache from 3.0.2 to 3.0.3 100 | - Bump shivammathur/setup-php from 2.18.1 to 2.19.0 101 | - Bump shivammathur/setup-php from 2.19.0 to 2.19.1 102 | - Bump actions/cache from 3.0.3 to 3.0.4 103 | - Bump shivammathur/setup-php from 2.19.1 to 2.20.0 104 | - Bump shivammathur/setup-php from 2.20.0 to 2.20.1 105 | - Bump actions/cache from 3.0.4 to 3.0.5 106 | - Bump shivammathur/setup-php from 2.20.1 to 2.21.0 107 | 108 | ## [1.3.4] - 2022-05-15 109 | 110 | ### Continuous Integrations 111 | 112 | - Bump `actions/cache` from 2.x.x to 3.0.0 113 | - Bump shivammathur/setup-php from 2.17.1 to 2.18.0 114 | - Bump codecov/codecov-action from 2.1.0 to 3.0.0 115 | - Bump shivammathur/setup-php from 2.18.0 to 2.18.1 116 | - Bump actions/cache from 3.0.0 to 3.0.2 117 | - Bump actions/checkout from 3.0.0 to 3.0.1 118 | - Bump actions/checkout from 3.0.1 to 3.0.2 119 | - Bump codecov/codecov-action from 3.0.0 to 3.1.0 120 | 121 | ### Miscellaneous Tasks 122 | 123 | - Update `config.allow-plugins` on the `composer` configs 124 | - Exclude `RequireAbstractOrFinal` rule from rulset 125 | 126 | ## [1.3.3] - 2022-03-03 127 | 128 | ### Documentation 129 | 130 | - Improve the dependabot configuration file 131 | 132 | ### Continuous Integrations 133 | 134 | - Change workflow actions versions to fixed versions 135 | - Add cache action for caching composer packages 136 | - Update `actions/checkout` from v2.x.x to v3.0.0 137 | - Bump `shivammathur/setup-php` to 2.17.1 138 | - Update `dependabot` prefixes on configuration 139 | 140 | ### Miscellaneous Tasks 141 | 142 | - Bump actions/checkout from 2.3.4 to 2.3.5 143 | - Bump actions/checkout from 2.3.5 to 2.4.0 144 | - Bump shivammathur/setup-php from 2.15.0 to 2.16.0 145 | - Bump actions/cache from 2.1.6 to 2.1.7 146 | - Update github funding configs 147 | - Solve github funding broken link issue 148 | - Bump shivammathur/setup-php from 2.16.0 to 2.17.0 149 | 150 | ## [1.3.2] - 2021-09-18 151 | 152 | ### Testing 153 | 154 | - Make sure endpoint changes in test mode 155 | 156 | ### Refactor 157 | 158 | - Change request data traversable types 159 | 160 | ### Documentation 161 | 162 | - Update old badges and add new ones 163 | 164 | ## [1.3.1] - 2021-09-17 165 | 166 | ### Refactor 167 | 168 | - Reduce max line length of codes to 80 169 | 170 | ### Miscellaneous Tasks 171 | 172 | - Add `phpstan/phpstan-beberlei-assert` package ^0.12.6 173 | - Update `nekofar/dev-tools` from ^1.0 to ^1.1 174 | 175 | ## [1.3.0] - 2021-09-17 176 | 177 | ### Bug Fixes 178 | 179 | - Solve `getCode` returning int instead of string 180 | 181 | ### Testing 182 | 183 | - Specify type hint for items of its traversable 184 | - Replace dynamic calls to static assertions 185 | 186 | ### Refactor 187 | 188 | - Add declaration for strict types 189 | - Reorder methods by their visibility 190 | - Replace ternary operators with conditions 191 | - Replace fully qualified class names by aliases 192 | - Specify type hint for items of its traversable 193 | - Replace docblock return types by typehints 194 | - Replace exception caching with throwable caching 195 | - Replace assignment in if conditions 196 | - Make `getAuthority` on `AbstractRequest` nullable 197 | - Solve incompatibility with coding standard automatically 198 | - Update method returns and params types 199 | - Improve conditional expression 200 | - Solve return type issue of `getAuthority` and `getAmount` 201 | - Update data array traversable type hints 202 | 203 | ### Continuous Integrations 204 | 205 | - Add new workflow for static analysis 206 | - Change code sniffer php to 7.3 207 | 208 | ### Miscellaneous Tasks 209 | 210 | - Update github sopncor confiquration 211 | - Replace old rules by new ruleset 212 | - Add `phpstan/phpstan-strict-rules` package ^0.12.11 213 | - Add `phpstan/phpstan-phpunit` package ^0.12.22 214 | - Add a configuration file for phpstan 215 | - Make throws json error on `sendData` 216 | - 1.3.0 217 | 218 | ## [1.2.0] - 2021-09-17 219 | 220 | ### Bug Fixes 221 | 222 | - Upgrade dependencies and phpunit 223 | - Remove version from composer config 224 | 225 | ### Documentation 226 | 227 | - Add new dependabot configuration file 228 | 229 | ### Continuous Integrations 230 | 231 | - Change php version to 8.0 232 | - Add new tests workflow 233 | - Remove the travis config file 234 | 235 | ### Miscellaneous Tasks 236 | 237 | - Add support for php eight to the composer 238 | - Upgrade omnipay/tests package to latest version 239 | - Upgrade squizlabs/php_codesniffer package to latest version 240 | - Upgrade php package to latest version 241 | - Solve issues caused by upgrading tests 242 | - Add config file for standard version 243 | - Remove skip tag from standard version config 244 | - 1.1.0 245 | - Add skip tag from standard version config 246 | - 1.1.1 247 | - Change version stability requirements 248 | - Remove composer lock file 249 | - Replace required dev packages by `nekofar/dev-tools` 250 | - Change php version to ^7.3 || ^8.0 251 | 252 | ## [1.0.4] - 2020-10-16 253 | 254 | ### Miscellaneous Tasks 255 | 256 | - Bump version to v1.0.4 257 | 258 | ## [1.0.3] - 2020-10-16 259 | 260 | ### Miscellaneous Tasks 261 | 262 | - Update composer lock dependencies 263 | - Normalize composer config 264 | 265 | 266 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Milad Nekofar 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Omnipay: ZarinPal 2 | 3 | **ZarinPal driver for the Omnipay PHP payment processing library** 4 | 5 | [![Packagist Version][icon-packagist]][link-packagist] 6 | [![PHP from Packagist][icon-php-version]][link-packagist] 7 | [![Packagist Downloads][icon-downloads]][link-packagist] 8 | [![Tests Status][icon-workflow]][link-workflow] 9 | [![Coverage Status][icon-coverage]][link-coverage] 10 | [![License][icon-license]][link-license] 11 | [![Twitter: nekofar][icon-twitter]][link-twitter] 12 | 13 | [Omnipay](https://github.com/thephpleague/omnipay) is a framework agnostic, multi-gateway payment 14 | processing library for PHP. This package implements ZarinPal support for Omnipay. 15 | 16 | ## Installation 17 | 18 | Omnipay is installed via [Composer](http://getcomposer.org/). To install, simply require 19 | `league/omnipay` and `nekofar/omnipay-zarinpal` with Composer: 20 | 21 | ``` 22 | composer require league/omnipay nekofar/omnipay-zarinpal 23 | ``` 24 | 25 | ## Basic Usage 26 | 27 | The following gateways are provided by this package: 28 | 29 | * ZarinPal 30 | 31 | For general usage instructions, please see the main [Omnipay](https://github.com/omnipay/omnipay) 32 | repository. 33 | 34 | ## Example 35 | 36 | ### Purchase 37 | 38 | The result will be a redirect to the gateway or bank. 39 | 40 | ```php 41 | use Omnipay\Omnipay; 42 | 43 | $gateway = Omnipay::create('ZarinPal'); 44 | $gateway->setMerchantId('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'); 45 | $gateway->setReturnUrl('https://www.example.com/return'); 46 | 47 | // Send purchase request 48 | $response = $gateway->purchase([ 49 | 'amount' => 100, 50 | 'description' => 'Some description' 51 | ])->send(); 52 | 53 | // Process response 54 | if ($response->isRedirect()) { 55 | // Redirect to offsite payment gateway 56 | $response->redirect(); 57 | } else { 58 | // Payment failed: display message to customer 59 | echo $response->getMessage(); 60 | } 61 | ``` 62 | 63 | On return, the usual completePurchase will provide the result of the transaction attempt. 64 | 65 | The final result includes the following methods to inspect additional details: 66 | 67 | ```php 68 | // Send purchase complete request 69 | $response = $gateway->completePurchase([ 70 | 'amount' => 100, 71 | 'authority' => $_REQUEST['Authority'], 72 | )->send(); 73 | 74 | // Process response 75 | if ($response->isSuccessful()) { 76 | // Payment was successful 77 | print_r($response); 78 | } else { 79 | // Payment failed: display message to customer 80 | echo $response->getMessage(); 81 | } 82 | ``` 83 | 84 | ### Testing 85 | 86 | ```sh 87 | composer test 88 | ``` 89 | 90 | ## Support 91 | 92 | If you are having general issues with Omnipay, we suggest posting on 93 | [Stack Overflow](http://stackoverflow.com/). Be sure to add the 94 | [omnipay tag](http://stackoverflow.com/questions/tagged/omnipay) so it can be easily found. 95 | 96 | If you want to keep up to date with release anouncements, discuss ideas for the project, 97 | or ask more detailed questions, there is also a [mailing list](https://groups.google.com/forum/#!forum/omnipay) which 98 | you can subscribe to. 99 | 100 | If you believe you have found a bug, please report it using the [GitHub issue tracker](https://github.com/nekofar/omnipay-zarinpal/issues), 101 | or better yet, fork the library and submit a pull request. 102 | 103 | [1]: https://packagist.org/packages/nekofar/omnipay-zarinpal 104 | [2]: https://github.com/nekofar/omnipay-zarinpal/blob/master/LICENSE 105 | [3]: https://travis-ci.com/nekofar/omnipay-zarinpal 106 | [4]: https://codecov.io/gh/nekofar/omnipay-zarinpal 107 | [5]: https://packagist.org/providers/php-http/client-implementation 108 | [6]: https://zarinpal.com 109 | [7]: https://twitter.com/nekofar 110 | 111 | [icon-packagist]: https://img.shields.io/packagist/v/nekofar/omnipay-zarinpal.svg 112 | [icon-php-version]: https://img.shields.io/packagist/php-v/nekofar/omnipay-zarinpal.svg 113 | [icon-workflow]: https://img.shields.io/github/actions/workflow/status/nekofar/omnipay-zarinpal/tests.yml 114 | [icon-coverage]: https://codecov.io/gh/nekofar/omnipay-zarinpal/graph/badge.svg 115 | [icon-downloads]: https://img.shields.io/packagist/dt/nekofar/omnipay-zarinpal 116 | [icon-license]: https://img.shields.io/github/license/nekofar/omnipay-zarinpal.svg 117 | [icon-twitter]: https://img.shields.io/badge/follow-%40nekofar-1DA1F2?logo=twitter&style=flat 118 | 119 | [link-packagist]: https://packagist.org/packages/nekofar/omnipay-zarinpal 120 | [link-twitter]: https://twitter.com/nekofar 121 | [link-coverage]: https://codecov.io/gh/nekofar/omnipay-zarinpal 122 | [link-workflow]: https://github.com/nekofar/omnipay-zarinpal/actions/workflows/tests.yml 123 | [link-coverage]: https://codecov.io/gh/nekofar/omnipay-zarinpal 124 | [link-license]: https://github.com/nekofar/omnipay-zarinpal/blob/master/LICENSE.md 125 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # configuration file for git-cliff (0.1.0) 2 | 3 | [changelog] 4 | # changelog header 5 | header = """ 6 | # Changelog\n 7 | All notable changes to this project will be documented in this file.\n 8 | """ 9 | # template for the changelog body 10 | # https://tera.netlify.app/docs/#introduction 11 | body = """ 12 | {% if version %}\ 13 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 14 | {% else %}\ 15 | ## [unreleased] 16 | {% endif %}\ 17 | {% for group, commits in commits | group_by(attribute="group") %} 18 | ### {{ group | upper_first }} 19 | {% for commit in commits %} 20 | - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\ 21 | {% endfor %} 22 | {% endfor %}\n 23 | """ 24 | # remove the leading and trailing whitespace from the template 25 | trim = true 26 | # changelog footer 27 | footer = """ 28 | 29 | """ 30 | 31 | [git] 32 | # parse the commits based on https://www.conventionalcommits.org 33 | conventional_commits = true 34 | # filter out the commits that are not conventional 35 | filter_unconventional = true 36 | # regex for preprocessing the commit messages 37 | commit_preprocessors = [ 38 | { pattern = '\((\w+\s)?#([0-9]+)\)', replace = ""}, 39 | ] 40 | # regex for parsing and grouping commits 41 | commit_parsers = [ 42 | { message = "^feat", group = "Features"}, 43 | { message = "^fix", group = "Bug Fixes"}, 44 | { message = "^docs\\(changelog\\)", skip = true}, 45 | { message = "^doc", group = "Documentation"}, 46 | { message = "^perf", group = "Performance", skip = true}, 47 | { message = "^refactor", group = "Refactor"}, 48 | { message = "^style", group = "Styling", skip = true}, 49 | { message = "^test", group = "Testing"}, 50 | { message = "^chore\\(release\\): prepare for", skip = true}, 51 | { message = "^chore", group = "Miscellaneous Tasks"}, 52 | { message = "^build", group = "Build Environment"}, 53 | { message = "^ci", group = "Continuous Integrations"}, 54 | { body = ".*security", group = "Security"}, 55 | ] 56 | # filter out the commits that are not matched by commit parsers 57 | filter_commits = true 58 | # glob pattern for matching git tags 59 | tag_pattern = "v[0-9]*" 60 | # regex for skipping tags 61 | skip_tags = "v0.1.0-beta.1" 62 | # regex for ignoring tags 63 | ignore_tags = "" 64 | # sort the tags chronologically 65 | date_order = false 66 | # sort the commits inside sections by oldest/newest order 67 | sort_commits = "oldest" 68 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | paths: 4 | - src 5 | - tests 6 | -------------------------------------------------------------------------------- /src/Gateway.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal; 12 | 13 | use Omnipay\Common\AbstractGateway; 14 | use Omnipay\Common\Message\RequestInterface; 15 | use Omnipay\ZarinPal\Message\PurchaseCompleteRequest; 16 | use Omnipay\ZarinPal\Message\PurchaseRequest; 17 | 18 | /** 19 | * Class Gateway 20 | */ 21 | class Gateway extends AbstractGateway 22 | { 23 | /** 24 | * Get gateway display name 25 | * 26 | * This can be used by carts to get the display name for each gateway. 27 | */ 28 | public function getName(): string 29 | { 30 | return 'ZarinPal'; 31 | } 32 | 33 | /** 34 | * @return array 35 | */ 36 | public function getDefaultParameters(): array 37 | { 38 | return [ 39 | 'testMode' => false, 40 | 'merchantId' => '', 41 | 'returnUrl' => '', 42 | ]; 43 | } 44 | 45 | /** 46 | */ 47 | public function getMerchantId(): string 48 | { 49 | return $this->getParameter('merchantId'); 50 | } 51 | 52 | /** 53 | */ 54 | public function getReturnUrl(): string 55 | { 56 | return $this->getParameter('returnUrl'); 57 | } 58 | 59 | /** 60 | */ 61 | public function setMerchantId(string $value): Gateway 62 | { 63 | return $this->setParameter('merchantId', $value); 64 | } 65 | 66 | /** 67 | * @return $this 68 | */ 69 | public function setReturnUrl(string $value) 70 | { 71 | return $this->setParameter('returnUrl', $value); 72 | } 73 | 74 | /** 75 | * @param array $params 76 | */ 77 | public function purchase(array $params = []): RequestInterface 78 | { 79 | return $this->createRequest(PurchaseRequest::class, $params); 80 | } 81 | 82 | /** 83 | * @param array $params 84 | */ 85 | public function completePurchase(array $params = []): RequestInterface 86 | { 87 | return $this->createRequest(PurchaseCompleteRequest::class, $params); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Message/AbstractRequest.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | use Omnipay\Common\Exception\InvalidResponseException; 14 | use Omnipay\Common\Message\AbstractRequest as BaseAbstractRequest; 15 | use Omnipay\Common\Message\ResponseInterface; 16 | use Throwable; 17 | 18 | use function json_decode; 19 | 20 | use const JSON_THROW_ON_ERROR; 21 | 22 | /** 23 | * Class AbstractRequest 24 | */ 25 | abstract class AbstractRequest extends BaseAbstractRequest 26 | { 27 | /** 28 | * Sandbox Endpoint URL 29 | * 30 | * @var string URL 31 | */ 32 | protected $testEndpoint = 'https://sandbox.zarinpal.com/pg/rest/WebGate'; 33 | 34 | /** 35 | * Live Endpoint URL 36 | * 37 | * @var string URL 38 | */ 39 | protected $liveEndpoint = 'https://www.zarinpal.com/pg/rest/WebGate'; 40 | 41 | /** 42 | */ 43 | abstract protected function createUri(string $endpoint): string; 44 | 45 | /** 46 | * @param array $data 47 | */ 48 | abstract protected function createResponse(array $data): ResponseInterface; 49 | 50 | /** 51 | * @throws \Omnipay\Common\Exception\InvalidRequestException 52 | */ 53 | public function getAmount(): string 54 | { 55 | $value = parent::getAmount(); 56 | 57 | if (!in_array($value, [null, ''], true)) { 58 | return $value; 59 | } 60 | 61 | return (string) $this->httpRequest->query->get('Amount'); 62 | } 63 | 64 | /** 65 | */ 66 | public function getMerchantId(): string 67 | { 68 | return $this->getParameter('merchantId'); 69 | } 70 | 71 | /** 72 | */ 73 | public function getAuthority(): ?string 74 | { 75 | $value = (string) $this->getParameter('authority'); 76 | 77 | if (!in_array($value, [null, ''], true)) { 78 | return $value; 79 | } 80 | 81 | return (string) $this->httpRequest->query->get('Authority'); 82 | } 83 | 84 | /** 85 | */ 86 | public function setMerchantId(string $value): AbstractRequest 87 | { 88 | return $this->setParameter('merchantId', $value); 89 | } 90 | 91 | /** 92 | */ 93 | public function setAuthority(string $value): AbstractRequest 94 | { 95 | return $this->setParameter('authority', $value); 96 | } 97 | 98 | /** 99 | */ 100 | public function getEmail(): string 101 | { 102 | return $this->getParameter('email'); 103 | } 104 | 105 | /** 106 | */ 107 | public function getMobile(): string 108 | { 109 | return $this->getParameter('mobile'); 110 | } 111 | 112 | /** 113 | */ 114 | public function setEmail(string $value): AbstractRequest 115 | { 116 | return $this->setParameter('email', $value); 117 | } 118 | 119 | /** 120 | */ 121 | public function setMobile(string $value): AbstractRequest 122 | { 123 | return $this->setParameter('mobile', $value); 124 | } 125 | 126 | /** 127 | * Send the request with specified data 128 | * 129 | * @param array $data The data to send. 130 | * 131 | * @throws \Omnipay\Common\Exception\InvalidResponseException 132 | * 133 | * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint 134 | * 135 | * @phpstan-ignore-next-line 136 | */ 137 | public function sendData($data): ResponseInterface 138 | { 139 | try { 140 | $httpResponse = $this->httpClient->request( 141 | 'POST', 142 | $this->createUri($this->getEndpoint()), 143 | [ 144 | 'Accept' => 'application/json', 145 | 'Content-type' => 'application/json', 146 | ], 147 | json_encode($data, JSON_THROW_ON_ERROR), 148 | ); 149 | $json = $httpResponse->getBody()->getContents(); 150 | $data = []; 151 | 152 | if (!in_array($json, [null, ''], true)) { 153 | $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); 154 | } 155 | 156 | return $this->response = $this->createResponse($data); 157 | } catch (Throwable $e) { 158 | throw new InvalidResponseException( 159 | sprintf( 160 | 'Error communicating with payment gateway: %s', 161 | $e->getMessage(), 162 | ), 163 | $e->getCode(), 164 | ); 165 | } 166 | } 167 | 168 | /** 169 | */ 170 | protected function getEndpoint(): string 171 | { 172 | return $this->getTestMode() 173 | ? $this->testEndpoint 174 | : $this->liveEndpoint; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/Message/AbstractResponse.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | use Omnipay\Common\Message\AbstractResponse as BaseAbstractResponse; 14 | 15 | /** 16 | * Class AbstractResponse 17 | */ 18 | abstract class AbstractResponse extends BaseAbstractResponse 19 | { 20 | /** 21 | * @var array 22 | */ 23 | private $errorCodes = [ 24 | '-1' => 'Information submitted is incomplete..', 25 | '-2' => 'Merchant ID or Acceptor IP is not correct.', 26 | '-3' => 'Amount should be above 100 Toman.', 27 | '-4' => 'Approved level of Acceptor is Lower than the silver.', 28 | '-11' => 'Request Not found.', 29 | '-12' => 'It is not possible to edit the request.', 30 | '-21' => 'Financial operations for this transaction was not found.', 31 | '-22' => 'Transaction is unsuccessful.', 32 | '-33' => 'Transaction amount does not match the amount paid.', 33 | '-34' => 'Limit the number of transactions or number has crossed the divide.', // phpcs:ignoree 34 | '-40' => 'There is no access to the method.', 35 | '-41' => 'Additional Data related to information submitted is invalid.', 36 | '-42' => 'The validity period of the payment id lifetime must be between 30 minutes to 45 days.', // phpcs:ignore 37 | '-54' => 'Request archived.', 38 | '100' => 'Operation was successful.', 39 | '101' => 'Operation was successful but verification operation on this transaction have already been done.', // phpcs:ignore 40 | ]; 41 | 42 | /** 43 | * Response Message 44 | * 45 | * @return string|null A response message from the payment gateway 46 | */ 47 | public function getMessage(): ?string 48 | { 49 | return $this->errorCodes[$this->getCode()] ?? parent::getMessage(); 50 | } 51 | 52 | /** 53 | * Response code 54 | * 55 | * @return string|null A response code from the payment gateway 56 | */ 57 | public function getCode(): ?string 58 | { 59 | return (string) ($this->data['Status'] ?? parent::getCode()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Message/PurchaseCompleteRequest.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | use Omnipay\Common\Message\ResponseInterface; 14 | 15 | /** 16 | * Class PurchaseCompleteRequest 17 | */ 18 | class PurchaseCompleteRequest extends AbstractRequest 19 | { 20 | /** 21 | * Get the raw data array for this message. The format of this varies from 22 | * gateway to gateway, but will usually be either an associative array, or 23 | * a SimpleXMLElement. 24 | * 25 | * @return array 26 | * 27 | * @throws \Omnipay\Common\Exception\InvalidRequestException 28 | */ 29 | public function getData(): array 30 | { 31 | // Validate required parameters before return data 32 | $this->validate('merchantId', 'amount', 'authority'); 33 | 34 | return [ 35 | 'MerchantID' => $this->getMerchantId(), 36 | 'Amount' => $this->getAmount(), 37 | 'Authority' => $this->getAuthority(), 38 | ]; 39 | } 40 | 41 | /** 42 | * Create an uri based on given endpoint. 43 | */ 44 | protected function createUri(string $endpoint): string 45 | { 46 | return $endpoint . '/PaymentVerification.json'; 47 | } 48 | 49 | /** 50 | * @param array $data 51 | */ 52 | protected function createResponse(array $data): ResponseInterface 53 | { 54 | return new PurchaseCompleteResponse($this, $data); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Message/PurchaseCompleteResponse.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | /** 14 | * Class PurchaseCompleteResponse 15 | */ 16 | class PurchaseCompleteResponse extends AbstractResponse 17 | { 18 | /** 19 | * Is the response successful? 20 | */ 21 | public function isSuccessful(): bool 22 | { 23 | return '100' === $this->getCode(); 24 | } 25 | 26 | /** 27 | * A reference provided by the gateway to represent this transaction. 28 | */ 29 | public function getTransactionReference(): ?string 30 | { 31 | return $this->data['RefID']; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Message/PurchaseRequest.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | use Omnipay\Common\Message\ResponseInterface; 14 | 15 | /** 16 | * Class PurchaseRequest 17 | */ 18 | class PurchaseRequest extends AbstractRequest 19 | { 20 | /** 21 | * Get the raw data array for this message. The format of this varies from 22 | * gateway to gateway, but will usually be either an associative array, or 23 | * a SimpleXMLElement. 24 | * 25 | * @return array 26 | * 27 | * @throws \Omnipay\Common\Exception\InvalidRequestException 28 | */ 29 | public function getData(): array 30 | { 31 | // Validate required parameters before return data 32 | $this->validate('merchantId', 'amount', 'description', 'returnUrl'); 33 | 34 | return [ 35 | 'MerchantID' => $this->getMerchantId(), 36 | 'Amount' => $this->getAmount(), 37 | 'Description' => $this->getDescription(), 38 | 'Email' => $this->getEmail(), 39 | 'Mobile' => $this->getMobile(), 40 | 'CallbackURL' => $this->getReturnUrl(), 41 | ]; 42 | } 43 | 44 | /** 45 | * Create an uri based on given endpoint. 46 | */ 47 | protected function createUri(string $endpoint): string 48 | { 49 | return $endpoint . '/PaymentRequest.json'; 50 | } 51 | 52 | /** 53 | * @param array $data 54 | */ 55 | protected function createResponse(array $data): ResponseInterface 56 | { 57 | return new PurchaseResponse($this, $data); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Message/PurchaseResponse.php: -------------------------------------------------------------------------------- 1 | 7 | */ 8 | 9 | declare(strict_types=1); 10 | 11 | namespace Omnipay\ZarinPal\Message; 12 | 13 | use Omnipay\Common\Message\RedirectResponseInterface as RedirectResponse; 14 | 15 | /** 16 | * Class PurchaseResponse 17 | */ 18 | class PurchaseResponse extends AbstractResponse implements RedirectResponse 19 | { 20 | /** 21 | * The embodied request object. 22 | * 23 | * @var \Omnipay\ZarinPal\Message\PurchaseRequest 24 | */ 25 | protected $request; 26 | 27 | /** 28 | * Sandbox Endpoint URL 29 | * 30 | * @var string URL 31 | */ 32 | protected $testEndpoint = 'https://sandbox.zarinpal.com/pg/StartPay/'; 33 | 34 | /** 35 | * Live Endpoint URL 36 | * 37 | * @var string URL 38 | */ 39 | protected $liveEndpoint = 'https://www.zarinpal.com/pg/StartPay/'; 40 | 41 | /** 42 | * Is the response successful? 43 | */ 44 | public function isSuccessful(): bool 45 | { 46 | return false; 47 | } 48 | 49 | /** 50 | * Does the response require a redirect? 51 | */ 52 | public function isRedirect(): bool 53 | { 54 | return isset($this->data['Authority']) 55 | && !in_array($this->data['Authority'], [null, ''], true); 56 | } 57 | 58 | /** 59 | * Gets the redirect target url. 60 | */ 61 | public function getRedirectUrl(): string 62 | { 63 | return $this->getEndpoint() . $this->data['Authority']; 64 | } 65 | 66 | /** 67 | */ 68 | protected function getEndpoint(): string 69 | { 70 | return $this->request->getTestMode() 71 | ? $this->testEndpoint 72 | : $this->liveEndpoint; 73 | } 74 | } 75 | --------------------------------------------------------------------------------