diff --git a/.github/actions/make-build/action.yml b/.github/actions/make-build/action.yml index e2b2a96..b191849 100644 --- a/.github/actions/make-build/action.yml +++ b/.github/actions/make-build/action.yml @@ -1,6 +1,11 @@ name: "Build Flutterwave WooCommerce" description: "Build Flutterwave WooCommerce plugin" +inputs: + signoz-api-key: + description: "API key for the SigNoz observability service" + required: true + outputs: release-filename: description: "The name of the release filename" @@ -16,6 +21,12 @@ runs: curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp + - name: "Inject SigNoz API key" + shell: bash + env: + SIGNOZ_API_KEY: ${{ inputs.signoz-api-key }} + run: | + sed -i "s|%%SIGNOZ_API_KEY%%|${SIGNOZ_API_KEY}|g" includes/util/class-flutterwave-signoz-logger.php - name: "Build the plugin" id: build_plugin shell: bash diff --git a/.github/workflows/package-publish.yml b/.github/workflows/package-publish.yml index ac2fbe4..e91f050 100644 --- a/.github/workflows/package-publish.yml +++ b/.github/workflows/package-publish.yml @@ -1,40 +1,42 @@ name: Publish to WordPress.org on: - release: - types: [released] + release: + types: [released] jobs: - tag: - name: New Release - runs-on: ubuntu-latest + tag: + name: New Release + runs-on: ubuntu-latest + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + steps: + - uses: act10ns/slack@v2 + with: + status: starting + if: ${{ always() }} + - name: Checkout code + uses: actions/checkout@v3 + - name: Setup Node.js environment + uses: actions/setup-node@v2.5.2 + with: + node-version-file: .nvmrc + - name: Build Plugin + uses: ./.github/actions/make-build + with: + signoz-api-key: ${{ secrets.SIGNOZ_API_KEY }} + - name: Install SVN + run: sudo apt-get update && sudo apt-get install -y subversion + - name: WordPress Plugin Deploy + id: deploy + uses: 10up/action-wordpress-plugin-deploy@stable env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - steps: - - uses: act10ns/slack@v2 - with: - status: starting - if: ${{ always() }} - - name: Checkout code - uses: actions/checkout@v3 - - name: Setup Node.js environment - uses: actions/setup-node@v2.5.2 - with: - node-version-file: .nvmrc - - name: Build Plugin - uses: ./.github/actions/make-build - - name: Install SVN - run: sudo apt-get update && sudo apt-get install -y subversion - - name: WordPress Plugin Deploy - id: deploy - uses: 10up/action-wordpress-plugin-deploy@stable - env: - SVN_USERNAME: ${{ secrets.SVN_USERNAME }} - SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} - SLUG: rave-woocommerce-payment-gateway - - name: push build status to Slack - uses: 8398a7/action-slack@v3 - with: - status: ${{ job.status }} - fields: repo,message,commit,author,action,eventName,ref,workflow,job,took,pullRequest - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - if: always() + SVN_USERNAME: ${{ secrets.SVN_USERNAME }} + SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }} + SLUG: rave-woocommerce-payment-gateway + - name: push build status to Slack + uses: 8398a7/action-slack@v3 + with: + status: ${{ job.status }} + fields: repo,message,commit,author,action,eventName,ref,workflow,job,took,pullRequest + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + if: always() diff --git a/composer.json b/composer.json index c704584..518bb58 100644 --- a/composer.json +++ b/composer.json @@ -27,6 +27,9 @@ "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true + }, + "platform": { + "php": "7.4.33" } }, "scripts": { diff --git a/composer.lock b/composer.lock index efcc65e..82ea6a5 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1bd5c32570738bfa2cc7288e85019974", + "content-hash": "80374ee24eeb123215e12b0de097cbd0", "packages": [], "packages-dev": [ { @@ -154,16 +154,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -171,11 +171,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -201,7 +202,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -209,29 +210,31 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -239,7 +242,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -263,26 +266,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -323,9 +327,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -442,28 +452,28 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.2", + "version": "1.3.4", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26" + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", - "reference": "bba5a9dfec7fcfbd679cfaf611d86b4d3759da26", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", "shasum": "" }, "require": { "phpcompatibility/php-compatibility": "^9.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "paragonie/random_compat": "dev-master", "paragonie/sodium_compat": "dev-master" }, "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." }, "type": "phpcodesniffer-standard", @@ -493,33 +503,53 @@ ], "support": { "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" }, - "time": "2022-10-25T01:46:02+00:00" + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-09-19T17:43:28+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", - "version": "2.1.4", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", - "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5" + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", - "reference": "b6c1e3ee1c35de6c41a511d5eb9bd03e447480a5", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", "shasum": "" }, "require": { "phpcompatibility/php-compatibility": "^9.0", - "phpcompatibility/phpcompatibility-paragonie": "^1.0" + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" }, "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." }, "type": "phpcodesniffer-standard", @@ -548,41 +578,60 @@ ], "support": { "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" }, - "time": "2022-10-24T09:00:36+00:00" + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-10-18T00:05:59+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.29", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", - "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -591,7 +640,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -620,7 +669,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -628,7 +677,7 @@ "type": "github" } ], - "time": "2023-09-19T04:57:46+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -873,45 +922,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.15", + "version": "9.6.34", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + "reference": "b36f02317466907a230d3aa1d34467041271ef4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b36f02317466907a230d3aa1d34467041271ef4a", + "reference": "b36f02317466907a230d3aa1d34467041271ef4a", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", + "sebastian/comparator": "^4.0.10", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.8", + "sebastian/global-state": "^5.0.8", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -956,7 +1005,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.34" }, "funding": [ { @@ -967,25 +1016,33 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2023-12-01T16:55:19+00:00" + "time": "2026-01-27T05:45:00+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -1020,7 +1077,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -1028,7 +1085,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -1143,16 +1200,16 @@ }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "4.0.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e4df00b9b3571187db2831ae9aada2c6efbd715d", + "reference": "e4df00b9b3571187db2831ae9aada2c6efbd715d", "shasum": "" }, "require": { @@ -1205,32 +1262,44 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.10" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2026-01-24T09:22:56+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", - "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", + "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", "shasum": "" }, "require": { - "nikic/php-parser": "^4.7", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1262,7 +1331,7 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" }, "funding": [ { @@ -1270,20 +1339,20 @@ "type": "github" } ], - "time": "2020-10-26T15:52:27+00:00" + "time": "2023-12-22T06:19:30+00:00" }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -1328,7 +1397,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -1336,7 +1405,7 @@ "type": "github" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", @@ -1403,16 +1472,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "4.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/14c6ba52f95a36c3d27c835d65efc7123c446e8c", + "reference": "14c6ba52f95a36c3d27c835d65efc7123c446e8c", "shasum": "" }, "require": { @@ -1468,28 +1537,40 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2025-09-24T06:03:27+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "5.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/b6781316bdcd28260904e7cc18ec983d0d2ef4f6", + "reference": "b6781316bdcd28260904e7cc18ec983d0d2ef4f6", "shasum": "" }, "require": { @@ -1532,32 +1613,44 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.8" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2025-08-10T07:10:35+00:00" }, { "name": "sebastian/lines-of-code", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", - "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", "shasum": "" }, "require": { - "nikic/php-parser": "^4.6", + "nikic/php-parser": "^4.18 || ^5.0", "php": ">=7.3" }, "require-dev": { @@ -1589,7 +1682,7 @@ "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" }, "funding": [ { @@ -1597,7 +1690,7 @@ "type": "github" } ], - "time": "2020-11-28T06:42:11+00:00" + "time": "2023-12-22T06:20:34+00:00" }, { "name": "sebastian/object-enumerator", @@ -1713,16 +1806,16 @@ }, { "name": "sebastian/recursion-context", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0", + "reference": "539c6691e0623af6dc6f9c20384c120f963465a0", "shasum": "" }, "require": { @@ -1764,28 +1857,40 @@ "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.6" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2025-08-10T06:57:39+00:00" }, { "name": "sebastian/resource-operations", - "version": "3.0.3", + "version": "3.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e", + "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e", "shasum": "" }, "require": { @@ -1797,7 +1902,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1818,8 +1923,7 @@ "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4" }, "funding": [ { @@ -1827,7 +1931,7 @@ "type": "github" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2024-03-14T16:00:52+00:00" }, { "name": "sebastian/type", @@ -1940,16 +2044,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.8.0", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5805f7a4e4958dbb5e944ef1e6edae0a303765e7", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -1959,18 +2063,13 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -2014,22 +2113,26 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2023-12-08T12:32:31+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -2058,7 +2161,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -2066,7 +2169,7 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2025-11-17T20:03:58+00:00" }, { "name": "woocommerce/woocommerce-sniffs", @@ -2165,16 +2268,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "1.1.0", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "224e4a1329c03d8bad520e3fc4ec980034a4b212" + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/224e4a1329c03d8bad520e3fc4ec980034a4b212", - "reference": "224e4a1329c03d8bad520e3fc4ec980034a4b212", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/41aaac462fbd80feb8dd129e489f4bbc53fe26b0", + "reference": "41aaac462fbd80feb8dd129e489f4bbc53fe26b0", "shasum": "" }, "require": { @@ -2182,12 +2285,14 @@ "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.0 || ^7.0 || ^8.0 || ^9.0" }, "require-dev": { - "yoast/yoastcs": "^2.3.0" + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "yoast/yoastcs": "^3.2.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "4.x-dev" } }, "autoload": { @@ -2219,20 +2324,24 @@ ], "support": { "issues": "https://github.com/Yoast/PHPUnit-Polyfills/issues", + "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2023-08-19T14:25:08+00:00" + "time": "2025-08-10T04:54:36+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": ">=7.4", "ext-json": "*" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "platform-overrides": { + "php": "7.4.33" + }, + "plugin-api-version": "2.6.0" } diff --git a/i18n/languages/rave-woocommerce-payment-gateway.pot b/i18n/languages/rave-woocommerce-payment-gateway.pot index 9cf6ebe..48c68c0 100644 --- a/i18n/languages/rave-woocommerce-payment-gateway.pot +++ b/i18n/languages/rave-woocommerce-payment-gateway.pot @@ -1,15 +1,15 @@ -# Copyright (C) 2025 Flutterwave Developers +# Copyright (C) 2026 Flutterwave Developers # This file is distributed under the MIT License. msgid "" msgstr "" -"Project-Id-Version: Flutterwave WooCommerce 2.3.6\n" +"Project-Id-Version: Flutterwave WooCommerce 3.2.0\n" "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/rave-woocommerce-payment-gateway\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"POT-Creation-Date: 2025-08-27T20:31:46+00:00\n" +"POT-Creation-Date: 2026-06-11T10:03:58+00:00\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "X-Generator: WP-CLI 2.12.0\n" "X-Domain: rave-woocommerce-payment-gateway\n" @@ -17,7 +17,7 @@ msgstr "" #. Plugin Name of the plugin #. Translators: %s Plugin name. #: rave-woocommerce-payment-gateway.php -#: includes/class-flw-wc-payment-gateway.php:230 +#: includes/class-flw-wc-payment-gateway.php:269 #: includes/views/html-admin-missing-woocommerce.php:15 msgid "Flutterwave WooCommerce" msgstr "" @@ -67,292 +67,293 @@ msgstr "" msgid "Diners" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:64 +#: includes/class-flw-wc-payment-gateway-event-handler.php:66 msgid "Payment initialized via Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:66 +#: includes/class-flw-wc-payment-gateway-event-handler.php:68 msgid "Your transaction reference: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:83 +#: includes/class-flw-wc-payment-gateway-event-handler.php:85 msgid "Attention: New order has been placed on hold because of incorrect payment amount or currency. Please, look into it." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid "Amount paid: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid "Order amount: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:84 +#: includes/class-flw-wc-payment-gateway-event-handler.php:86 msgid " Reference: " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:124 +#. translators: 1: payment reference 2: transaction reference +#: includes/class-flw-wc-payment-gateway-event-handler.php:97 +#: includes/class-flw-wc-payment-gateway-subscriptions.php:163 +#, php-format +msgid "Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)" +msgstr "" + +#: includes/class-flw-wc-payment-gateway-event-handler.php:145 msgid "The payment failed on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:129 +#: includes/class-flw-wc-payment-gateway-event-handler.php:150 msgid "Reason for Failure : " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:141 +#: includes/class-flw-wc-payment-gateway-event-handler.php:164 msgid "Confirming payment on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:151 +#: includes/class-flw-wc-payment-gateway-event-handler.php:174 msgid "An error occured while confirming payment on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:156 +#: includes/class-flw-wc-payment-gateway-event-handler.php:179 msgid "Attention: New order has been placed on hold because we could not confirm the payment. Please, look into it." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:172 +#: includes/class-flw-wc-payment-gateway-event-handler.php:196 msgid "The customer clicked on the cancel button on Checkout." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:174 +#: includes/class-flw-wc-payment-gateway-event-handler.php:198 msgid "Attention: Customer clicked on the cancel button on the payment gateway. We have updated the order to cancelled status. " msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:175 +#: includes/class-flw-wc-payment-gateway-event-handler.php:199 msgid "Please, confirm from the order notes that there is no note of a successful transaction. If there is, this means that the user was debited and you either have to give value for the transaction or refund the customer." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:189 +#: includes/class-flw-wc-payment-gateway-event-handler.php:213 msgid "The payment didn't return a valid response. It could have timed out or abandoned by the customer on Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:194 -msgid "Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Rave support team at hi@flutterwave.com to confirm the payment." +#: includes/class-flw-wc-payment-gateway-event-handler.php:218 +msgid "Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Flutterwave support team at hi@flutterwave.com to confirm the payment." msgstr "" -#: includes/class-flw-wc-payment-gateway-event-handler.php:195 +#: includes/class-flw-wc-payment-gateway-event-handler.php:219 msgid "Payment Reference: " msgstr "" -#. translators: 1: payment reference 2: transaction reference -#: includes/class-flw-wc-payment-gateway-subscriptions.php:163 -#, php-format -msgid "Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)" -msgstr "" - -#: includes/class-flw-wc-payment-gateway.php:154 +#: includes/class-flw-wc-payment-gateway.php:162 msgid "allows you to accept payment from cards and bank accounts in multiple currencies. You can also accept payment offline via USSD and POS." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:234 +#: includes/class-flw-wc-payment-gateway.php:273 msgid "Webhook Instruction" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:238 +#: includes/class-flw-wc-payment-gateway.php:277 msgid "Please copy this webhook URL and paste on the webhook section on your dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:259 +#: includes/class-flw-wc-payment-gateway.php:298 msgid "Enable/Disable" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:260 +#: includes/class-flw-wc-payment-gateway.php:299 msgid "Enable Flutterwave" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:262 +#: includes/class-flw-wc-payment-gateway.php:301 msgid "Enable Flutterwave as a payment option on the checkout page" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:267 +#: includes/class-flw-wc-payment-gateway.php:306 msgid "Enter Secret Hash" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:269 +#: includes/class-flw-wc-payment-gateway.php:308 msgid "Please change from default hash and ensure that SECRET HASH is the same with the one on your Flutterwave dashboard" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:273 +#: includes/class-flw-wc-payment-gateway.php:312 msgid "Payment method title" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:275 -#: includes/class-flw-wc-payment-gateway.php:281 +#: includes/class-flw-wc-payment-gateway.php:314 +#: includes/class-flw-wc-payment-gateway.php:320 msgid "Optional" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:279 +#: includes/class-flw-wc-payment-gateway.php:318 msgid "Payment method description" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:285 +#: includes/class-flw-wc-payment-gateway.php:324 msgid "Test Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:287 +#: includes/class-flw-wc-payment-gateway.php:326 msgid "Required! Enter your Flutterwave test public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:291 +#: includes/class-flw-wc-payment-gateway.php:330 msgid "Test Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:293 +#: includes/class-flw-wc-payment-gateway.php:332 msgid "Required! Enter your Flutterwave test secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:297 +#: includes/class-flw-wc-payment-gateway.php:336 msgid "Live Public Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:299 +#: includes/class-flw-wc-payment-gateway.php:338 msgid "Required! Enter your Flutterwave live public key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:303 +#: includes/class-flw-wc-payment-gateway.php:342 msgid "Live Secret Key" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:305 +#: includes/class-flw-wc-payment-gateway.php:344 msgid "Required! Enter your Flutterwave live secret key here" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:309 +#: includes/class-flw-wc-payment-gateway.php:348 msgid "Payment Style on checkout" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:311 +#: includes/class-flw-wc-payment-gateway.php:350 msgid "Optional - Choice of payment style to use. Either inline or redirect. (Default: inline)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:313 +#: includes/class-flw-wc-payment-gateway.php:352 msgctxt "payment_style" msgid "Popup(Keep payment experience on the website)" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:314 +#: includes/class-flw-wc-payment-gateway.php:353 msgctxt "payment_style" msgid "Redirect" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:319 +#: includes/class-flw-wc-payment-gateway.php:358 msgid "Autocomplete Order After Payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:320 +#: includes/class-flw-wc-payment-gateway.php:359 msgid "Autocomplete Order" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:323 +#: includes/class-flw-wc-payment-gateway.php:362 msgid "If enabled, the order will be marked as complete after successful payment" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:328 +#: includes/class-flw-wc-payment-gateway.php:367 msgid "Payment Options" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:330 +#: includes/class-flw-wc-payment-gateway.php:369 msgid "Optional - Choice of payment method to use. Card, Account etc." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:332 +#: includes/class-flw-wc-payment-gateway.php:371 msgctxt "payment_options" msgid "All" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:333 +#: includes/class-flw-wc-payment-gateway.php:372 msgctxt "payment_options" msgid "Card Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:334 +#: includes/class-flw-wc-payment-gateway.php:373 msgctxt "payment_options" msgid "Account Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:335 +#: includes/class-flw-wc-payment-gateway.php:374 msgctxt "payment_options" msgid "USSD Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:336 +#: includes/class-flw-wc-payment-gateway.php:375 msgctxt "payment_options" msgid "QR Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:337 +#: includes/class-flw-wc-payment-gateway.php:376 msgctxt "payment_options" msgid "Mpesa Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:338 +#: includes/class-flw-wc-payment-gateway.php:377 msgctxt "payment_options" msgid "Ghana MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:339 +#: includes/class-flw-wc-payment-gateway.php:378 msgctxt "payment_options" msgid "Rwanda MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:340 +#: includes/class-flw-wc-payment-gateway.php:379 msgctxt "payment_options" msgid "Zambia MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:341 +#: includes/class-flw-wc-payment-gateway.php:380 msgctxt "payment_options" msgid "Tanzania MM Only" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:346 +#: includes/class-flw-wc-payment-gateway.php:385 msgid "Mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:347 +#: includes/class-flw-wc-payment-gateway.php:386 msgid "Live mode" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:349 +#: includes/class-flw-wc-payment-gateway.php:388 msgid "Check this box if you're using your live keys." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:354 -#: includes/class-flw-wc-payment-gateway.php:355 +#: includes/class-flw-wc-payment-gateway.php:393 +#: includes/class-flw-wc-payment-gateway.php:394 msgid "Disable Logging" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:357 +#: includes/class-flw-wc-payment-gateway.php:396 msgid "Check this box if you're disabling logging." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:362 -#: includes/class-flw-wc-payment-gateway.php:363 +#: includes/class-flw-wc-payment-gateway.php:401 +#: includes/class-flw-wc-payment-gateway.php:402 msgid "Disable Barter" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:365 +#: includes/class-flw-wc-payment-gateway.php:404 msgid "Check the box if you want to disable barter." msgstr "" #. translators: %s: url -#: includes/class-flw-wc-payment-gateway.php:456 +#: includes/class-flw-wc-payment-gateway.php:502 #, php-format msgid "Flutterwave is enabled, but the API keys are not set. Please set your Flutterwave API keys to be able to accept payments." msgstr "" #. translators: %s: shop cart url -#: includes/class-flw-wc-payment-gateway.php:493 +#: includes/class-flw-wc-payment-gateway.php:538 #, php-format msgid "Sorry, your session has expired. Return to shop" msgstr "" -#: includes/class-flw-wc-payment-gateway.php:507 +#: includes/class-flw-wc-payment-gateway.php:552 msgid "We were unable to process your order, please try again." msgstr "" -#: includes/class-flw-wc-payment-gateway.php:582 +#: includes/class-flw-wc-payment-gateway.php:633 msgid "Order Payment" msgstr "" diff --git a/includes/class-flutterwave.php b/includes/class-flutterwave.php index 049be44..2b3374f 100644 --- a/includes/class-flutterwave.php +++ b/includes/class-flutterwave.php @@ -18,7 +18,7 @@ final class Flutterwave { * * @var string */ - public string $version = '3.1.0'; + public string $version = '3.2.0'; /** * Plugin API version. @@ -214,6 +214,5 @@ public static function plugin_action_links( array $links ): array { array_unshift( $links, "Settings" ); return $links; - } } diff --git a/includes/class-flw-wc-payment-gateway-event-handler.php b/includes/class-flw-wc-payment-gateway-event-handler.php index eb2ec9e..acca720 100644 --- a/includes/class-flw-wc-payment-gateway-event-handler.php +++ b/includes/class-flw-wc-payment-gateway-event-handler.php @@ -2,7 +2,7 @@ /** * The client-specific functionality of the plugin. * - * This class handles all events from the Rave class + * This class handles all events from the Flutterwave class * * @link https://flutterwave.com * @since 2.3.2 @@ -14,10 +14,12 @@ declare(strict_types=1); require FLW_WC_DIR_PATH . 'includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php'; +require_once FLW_WC_DIR_PATH . 'includes/util/class-flutterwave-signoz-logger.php'; use Flutterwave\WooCommerce\Contracts\FLW_WC_Payment_Gateway_Event_Handler_Interface; +use Flutterwave\WooCommerce\Util\Flutterwave_Signoz_Logger; /** - * This is the class that handles all events from the Rave class + * This is the class that handles all events from the Flutterwave class * */ class FLW_WC_Payment_Gateway_Event_Handler implements FLW_WC_Payment_Gateway_Event_Handler_Interface { /** @@ -89,7 +91,14 @@ public function on_successful( object $transaction_data ) { } else { $this->order->payment_complete( $this->order->get_id() ); $payment_method = $this->order->get_payment_method(); - + $this->order->add_order_note( + sprintf( + /* translators: 1: payment reference 2: transaction reference */ + __( 'Payment via Flutterwave successful (Payment Reference: %1$s, Transaction Reference: %2$s)', 'rave-woocommerce-payment-gateway' ), + $transaction_data->flw_ref, + $transaction_data->tx_ref + ) + ); $flw_settings = get_option( 'woocommerce_' . $payment_method . '_settings' ); if ( isset( $flw_settings['autocomplete_order'] ) && 'yes' === $flw_settings['autocomplete_order'] ) { @@ -101,6 +110,18 @@ public function on_successful( object $transaction_data ) { $customer_note = 'Thank you for your order.
'; $customer_note .= 'Your payment was successful, we are now processing your order.'; $this->order->add_order_note( $customer_note, 1 ); + + $is_production = Flutterwave_Signoz_Logger::instance()->get_current_environment() === 'production'; + + if ( $is_production ) { + Flutterwave_Signoz_Logger::instance()->track_transaction( + $transaction_data->tx_ref, + $transaction_data->currency, + (float) $transaction_data->amount, + $transaction_data->payment_type ?? 'card', + (float) $transaction_data->app_fee ?? 0 + ); + } } wc_add_notice( $customer_note, 'notice' ); // get order_id from the txref. @@ -124,10 +145,12 @@ public function on_failure( object $transaction_data ) { $this->order->add_order_note( esc_html__( 'The payment failed on Flutterwave', 'rave-woocommerce-payment-gateway' ) ); $customer_note = 'Your payment failed. '; $customer_note .= 'Please, try again or use another Payment Method on the modal.'; - $reason = $transaction_data->processor_response ?? ' - '; + $reason = $transaction_data->processor_response ?? $transaction_data->message ?? 'Payment processing failed'; $this->order->add_order_note( esc_html__( 'Reason for Failure : ', 'rave-woocommerce-payment-gateway' ) . $reason ); + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_FAILED', (string) $reason ); + wc_add_notice( $customer_note, 'notice' ); } @@ -156,9 +179,10 @@ public function on_requery_error( $requery_response ) { $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not confirm the payment. Please, look into it.', 'rave-woocommerce-payment-gateway' ) . '
'; $admin_note .= esc_html( 'Payment Responce: ' ) . $requery_response->message; + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_FAILED', (string) $requery_response->message ?? 'Payment Requery Failed' ); + $this->order->add_order_note( $customer_note, 1 ); $this->order->add_order_note( $admin_note ); - wc_add_notice( $customer_note, 'notice' ); } @@ -191,12 +215,13 @@ public function on_timeout( string $transaction_reference, object $data ) { $customer_note = 'Thank you for your order.
'; $customer_note .= 'We had an issue confirming your payment, but we have put your order on-hold. '; $customer_note .= esc_html__( 'Please, contact us for information regarding this order.', 'woocomerce-rave' ); - $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Rave support team at hi@flutterwave.com to confirm the payment.', 'rave-woocommerce-payment-gateway' ) . '
'; + $admin_note = esc_html__( 'Attention: New order has been placed on hold because we could not get a definite response from the payment gateway. Kindly contact the Flutterwave support team at hi@flutterwave.com to confirm the payment.', 'rave-woocommerce-payment-gateway' ) . '
'; $admin_note .= esc_html__( 'Payment Reference: ', 'rave-woocommerce-payment-gateway' ) . $transaction_reference; + Flutterwave_Signoz_Logger::instance()->track_error( 'PAYMENT_CONFIRMATION_TIMEOUT', 'Payment Confirmation timed out' ); + $this->order->add_order_note( $customer_note, 1 ); $this->order->add_order_note( $admin_note ); - wc_add_notice( $customer_note, 'notice' ); } @@ -211,5 +236,3 @@ public function on_webhook( string $event_type, object $event_data ) { // TODO: Save the event data to clients database. } } - - diff --git a/includes/class-flw-wc-payment-gateway-subscriptions.php b/includes/class-flw-wc-payment-gateway-subscriptions.php index 4c4aa3b..78aee24 100644 --- a/includes/class-flw-wc-payment-gateway-subscriptions.php +++ b/includes/class-flw-wc-payment-gateway-subscriptions.php @@ -28,7 +28,7 @@ class FLW_WC_Payment_Gateway_Subscriptions extends FLW_WC_Payment_Gateway { /** - * Constructor + * Constructor. */ public function __construct() { @@ -183,5 +183,3 @@ public function process_subscription_payment( $order = '', $amount = 0 ) { return new WP_Error( 'flutterwave_error', 'This subscription can\'t be renewed automatically. The customer will have to login to his account to renew his subscription' ); } } - - diff --git a/includes/class-flw-wc-payment-gateway.php b/includes/class-flw-wc-payment-gateway.php index ac0407c..d382f1f 100644 --- a/includes/class-flw-wc-payment-gateway.php +++ b/includes/class-flw-wc-payment-gateway.php @@ -26,11 +26,13 @@ require_once __DIR__ . '/client/class-flw-wc-payment-gateway-request.php'; require_once __DIR__ . '/client/class-flw-wc-payment-gateway-sdk.php'; require_once __DIR__ . '/util/class-flutterwave-logger.php'; +require_once __DIR__ . '/util/class-flutterwave-signoz-logger.php'; use Flutterwave\WooCommerce\Client\Flw_WC_Payment_Gateway_Request; use Flutterwave\WooCommerce\Client\FLW_WC_Payment_Gateway_Sdk as FlwSdk; use FLW_WC_Payment_Gateway_Event_Handler as FlwEventHandler; use Flutterwave\WooCommerce\Util\Flutterwave_Logger; +use Flutterwave\WooCommerce\Util\Flutterwave_Signoz_Logger; /** * Main Flutterwave Gateway Class @@ -97,6 +99,12 @@ class FLW_WC_Payment_Gateway extends WC_Payment_Gateway { * @var Flutterwave_Logger the logger */ private Flutterwave_Logger $logger; + /** + * SigNoz observability logger. + * + * @var Flutterwave_Signoz_Logger + */ + private Flutterwave_Signoz_Logger $signoz_logger; /** * Flutterwave Sdk * @@ -206,11 +214,12 @@ public function __construct() { $this->secret_key = $this->live_secret_key; } - $this->logger = Flutterwave_Logger::instance(); - $this->sdk = new FlwSdk( $this->secret_key, self::$log_enabled ); + $this->logger = Flutterwave_Logger::instance(); + $this->signoz_logger = Flutterwave_Signoz_Logger::instance(); + $this->sdk = new FlwSdk( $this->secret_key, self::$log_enabled ); + add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'send_app_created_event' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) ); - } /** @@ -222,6 +231,36 @@ public function get_secret_key(): string { return $this->secret_key; } + /** + * Fire the app.created SigNoz event using the freshly-saved settings. + * Hooked into woocommerce_update_options_payment_gateways_{id} so it runs + * after process_admin_options() has persisted the new values to the DB. + * + * @return void + */ + public function send_app_created_event(): void { + $settings = get_option( 'woocommerce_rave_settings', array() ); + + if ( isset( $settings['merchant_id'] ) && $settings['app_registered'] ) { + return; + } + + $go_live = $settings['go_live'] ?? 'no'; + $public_key = 'yes' === $go_live + ? ( $settings['live_public_key'] ?? '' ) + : ( $settings['test_public_key'] ?? '' ); + + if ( empty( $public_key ) ) { + return; + } + + $this->logger->info( 'Starting Application Integration' ); + $merchant_info = $this->signoz_logger->init( $public_key ); + $this->signoz_logger->track_app_created( $public_key, $merchant_info ); + $this->signoz_logger->mark_app_registered(); + $this->logger->info( 'Successfully Registered Application Integration' ); + } + /** * WooCommerce admin settings override. */ @@ -368,7 +407,6 @@ public function init_form_fields() { ), ); - } /** @@ -403,12 +441,18 @@ public function process_payment( $order_id ) { * @return array|void */ public function process_redirect_payments( $order_id ) { - include_once dirname( __FILE__ ) . '/client/class-flw-wc-payment-gateway-request.php'; + include_once __DIR__ . '/client/class-flw-wc-payment-gateway-request.php'; $order = wc_get_order( $order_id ); try { $flutterwave_request = ( new FLW_WC_Payment_Gateway_Request() )->get_prepared_payload( $order, $this->get_secret_key() ); + $this->signoz_logger->track_request_sent( + 'POST', + $flutterwave_request['tx_ref'], + '/payments', + $this->logger + ); } catch ( \InvalidArgumentException $flw_e ) { wc_add_notice( $flw_e, 'error' ); // redirect user to check out page. @@ -421,9 +465,11 @@ public function process_redirect_payments( $order_id ) { $flutterwave_request['payment_options'] = $this->payment_options; $custom_nonce = wp_create_nonce(); $flutterwave_request['redirect_url'] = $flutterwave_request['redirect_url'] . '&_wpnonce=' . $custom_nonce; - $sdk = $this->sdk->set_event_handler( new FlwEventHandler( $order ) ); + + $sdk = $this->sdk->set_event_handler( new FlwEventHandler( $order ) ); $response = $sdk->get_client()->request( $this->sdk::$standard_inline_endpoint, 'POST', $flutterwave_request ); + if ( ! is_wp_error( $response ) ) { $response = json_decode( $response['body'] ); return array( @@ -458,7 +504,6 @@ public function admin_notices(): void { ); } } - } /** @@ -537,6 +582,12 @@ public function payment_scripts() { $custom_nonce = wp_create_nonce(); $redirect_url = ''; + $this->signoz_logger->track_request_sent( + 'GET', + $txnref, + '/inline' + ); + $flutterwave_woo_url = WC()->api_request_url( 'FLW_WC_Payment_Gateway' ); // Parse the base URL to check for existing query parameters. @@ -620,6 +671,12 @@ public function flw_verify_payment() { die(); } + Flutterwave_Signoz_Logger::instance()->track_request_sent( + 'GET', + $txn_ref, + '/transactions/verify_by_reference?tx_ref=' + ); + $sdk->set_event_handler( new FlwEventHandler( $order ) )->requery_transaction( $txn_ref ); $redirect_url = $this->get_return_url( $order ); @@ -656,6 +713,10 @@ public function flutterwave_webhooks() { if ( $signature !== $local_signature ) { $this->logger->info( 'Faudulent Webhook Notification Attempt [Access Restricted]' ); + $this->signoz_logger->track_error( + 'WEBHOOK_SIGNATURE_MISMATCH', + 'Webhook signature mismatch. Access Denied. Hash does not match.' + ); wp_send_json( array( 'status' => 'error', @@ -670,6 +731,10 @@ public function flutterwave_webhooks() { $event = json_decode( $event ); if ( empty( $event->event ) && empty( $event->data ) ) { + $this->signoz_logger->track_error( + 'WEBHOOK_BODY_DEFORMED', + 'Webhook body is malformed. Missing event or data properties.' + ); wp_send_json( array( 'status' => 'error', @@ -714,6 +779,10 @@ public function flutterwave_webhooks() { $order = wc_get_order( $order_id ); if ( ! $order ) { + $this->signoz_logger->track_error( + 'INVALID_ORDER_REFERENCE_FROM_WEBHOOK', + 'Webhook sent an invalid order reference. No order found with ID: ' . $order_id + ); wp_send_json( array( 'status' => 'error', @@ -751,6 +820,12 @@ public function flutterwave_webhooks() { ); } + Flutterwave_Signoz_Logger::instance()->track_request_sent( + 'GET', + $txn_ref, + '/transactions/verify_by_reference?tx_ref=' + ); + $sdk->set_event_handler( new FlwEventHandler( $order ) )->webhook_verify( $event_type, $event_data ); wp_send_json( array( diff --git a/includes/client/class-flw-wc-payment-gateway-sdk.php b/includes/client/class-flw-wc-payment-gateway-sdk.php index 7fa9761..69b8162 100644 --- a/includes/client/class-flw-wc-payment-gateway-sdk.php +++ b/includes/client/class-flw-wc-payment-gateway-sdk.php @@ -88,7 +88,6 @@ final class FLW_WC_Payment_Gateway_Sdk { public function __construct( string $secret_key, bool $log_enabled = false ) { $this->client = new FLW_WC_Payment_Gateway_Client( $secret_key, $log_enabled ); $this->logger = wc_get_logger(); - return $this; } /** @@ -143,7 +142,7 @@ private function prepare_html( array $clean_data, int $order_id ): string { '', '', ' ', - ' ', + ' ', '', '', ); @@ -207,7 +206,7 @@ public function render_modal( array $data, int $order_id ) { * @return void The requery transaction. */ public function requery_transaction( string $tx_ref ) { - $this->requery_count ++; + ++$this->requery_count; $this->logger->notice( 'Requerying Transaction....' . $tx_ref ); if ( isset( $this->event_handler ) ) { @@ -234,7 +233,6 @@ public function requery_transaction( string $tx_ref ) { // TODO: handle request errors. $this->logger->notice( 'Transaction Requeried Failed. Awaiting Webhook Verification...' ); } - } /** @@ -258,7 +256,6 @@ public function webhook_verify( string $event_type, object $event_data ) { } else { $this->logger->notice( 'Webhook Verification Failed' ); } - } /** diff --git a/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php b/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php index fea3a6d..3f12857 100644 --- a/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php +++ b/includes/contracts/class-flw-wc-payment-gateway-event-handler-interface.php @@ -18,42 +18,42 @@ interface FLW_WC_Payment_Gateway_Event_Handler_Interface { * * @param object $initialization_data This is the initial transaction data as passed. * */ - public function on_init( object $initialization_data); + public function on_init( object $initialization_data ); /** * This is called only when a transaction is successful * * @param object $transaction_data This is the transaction data as returned from the Rave payment. gateway. * */ - public function on_successful( object $transaction_data); + public function on_successful( object $transaction_data ); /** * This is called only when a transaction failed * * @param object $transaction_data This is the transaction data as returned from the Rave payment gateway. * */ - public function on_failure( object $transaction_data); + public function on_failure( object $transaction_data ); /** * This is called when a transaction is requeryed from the payment gateway * * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * */ - public function on_requery( string $transaction_reference); + public function on_requery( string $transaction_reference ); /** * This is called a transaction requery returns with an error * * @param string $requery_response This is the error response gotten from the Rave payment gateway requery call. * */ - public function on_requery_error( $requery_response); + public function on_requery_error( $requery_response ); /** * This is called when a transaction is canceled by the user * * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * */ - public function on_cancel( string $transaction_reference); + public function on_cancel( string $transaction_reference ); /** * This is called when a transaction doesn't return with a success or a failure response. @@ -61,7 +61,7 @@ public function on_cancel( string $transaction_reference); * @param string $transaction_reference This is the transaction reference as returned from the Rave payment gateway. * @param object $data This is the data returned from the requery call. * */ - public function on_timeout( string $transaction_reference, object $data); + public function on_timeout( string $transaction_reference, object $data ); /** * This is called when a webhook is received diff --git a/includes/util/class-flutterwave-signoz-logger.php b/includes/util/class-flutterwave-signoz-logger.php new file mode 100644 index 0000000..8cd8693 --- /dev/null +++ b/includes/util/class-flutterwave-signoz-logger.php @@ -0,0 +1,311 @@ +). + * + * @var string + */ + private string $app_id = ''; + + /** + * Merchant environment: "production" or "sandbox". + * + * @var string + */ + private string $environment = 'sandbox'; + + /** + * Private constructor — use instance(). + */ + private function __construct() {} + + /** + * Get or create the singleton. + * + * @return self + */ + public static function instance(): self { + if ( null === self::$instance ) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * Get the current Flutterwave WooCommerce settings. + * + * @return array + */ + public function get_settings(): array { + return get_option( 'woocommerce_rave_settings', array() ); + } + + /** + * Configure the logger for a specific merchant public key and environment. + * Must be called before any track_* methods. + * + * @param string $public_key Merchant Flutterwave public key. + * @return string + */ + public function init( string $public_key ): string { + $merchant_id = $this->get_merchant_id( $public_key ); + if ( $merchant_id ) { + $merchant_id = str_replace( ' ', '_', $merchant_id ); + } + $this->app_id = $merchant_id ?? $public_key; + $this->environment = $this->get_current_environment(); + $new_options = $this->get_settings(); + $new_options['merchant_id'] = $this->app_id; + $new_options['app_registered'] = false; + update_option( 'woocommerce_rave_settings', $new_options ); + return $merchant_id; + } + + /** + * Get the merchant's Flutterwave account name using the public key. + * + * @param string $public_key Merchant Flutterwave public key. + * @return string|null Account name or null on failure. + */ + public function get_merchant_id( string $public_key ): ?string { + $settings = $this->get_settings(); + + if ( isset( $settings['merchant_id'] ) && $settings['merchant_id'] ) { + return $settings['merchant_id']; + } + // Extract the merchant ID from the public key (assuming format "FLWPUBK--..."). + // Make a request to https://api.ravepay.co/flwv3-pug/getpaidx/api/mercinfo?PBFPubKey=$public_key. + $url = 'https://api.ravepay.co/flwv3-pug/getpaidx/api/mercinfo?PBFPubKey=' . $public_key; + $response = wp_remote_get( $url ); + + if ( is_wp_error( $response ) ) { + return null; + } + + $body = wp_remote_retrieve_body( $response ); + $body = json_decode( $body, true ); + + return $body['mn'] ?? null; + } + + /** + * Fire the `app.created` event. + * Should be called once when the merchant first configures their keys. + * + * @param string $public_key Merchant Flutterwave public key. + * @param string $merchant_info Merchants actual name. + * @return void + */ + public function track_app_created( string $public_key, string $merchant_info ): void { + if ( empty( $public_key ) && empty( $merchant_info ) ) { + return; + } + $this->app_id = $merchant_info; + $this->send( + 'app.created', + array( + 'app_id' => $this->app_id, + 'client_id' => null, + 'public_key' => $public_key, + 'library' => self::LIBRARY, + 'library_version' => $this->library_version(), + ) + ); + } + + /** + * Mark App as Registered. + * + * @return void + */ + public function mark_app_registered() { + $settings = $this->get_settings(); + $new_settings = $settings; + $new_settings['app_registered'] = true; + update_option( 'woocommerce_rave_settings', $new_settings ); + } + + /** + * Get an Application Identifier. + */ + public function get_app_id(): string { + $settings = $this->get_settings(); + return $settings['merchant_id']; + } + + /** + * Get the current environment. + * + * @return string + */ + public function get_current_environment(): string { + $settings = $this->get_settings(); + return 'yes' === $settings['go_live'] ? 'production' : 'sandbox'; + } + + /** + * Fire the `request.sent` event when a payment request is initiated. + * + * @param string $method Payment method (e.g. "card"). + * @param string $reference Transaction reference (tx_ref). + * @param string $path Request path (e.g. "/v3/charges"). + * @param null $logger Log to the wc logger instance for flutterwave woocoomerce. + * @return void + */ + public function track_request_sent( string $method, string $reference, string $path, $logger = null ): void { + $payload = array( + 'app_id' => $this->get_app_id(), + 'environment' => $this->get_current_environment(), + 'api_version' => 'v3', + 'library_version' => $this->library_version(), + 'method' => $method, + 'path' => $path, + 'reference' => $reference, + ); + + $cache_key = 'signoz_event:request_sent:' . md5( wp_json_encode( $payload ) ); + + if ( get_transient( $cache_key ) ) { + return; // Already sent recently. + } + + if ( ! is_null( $logger ) ) { + $logger->info( 'request.sent: ' . wp_json_encode( $payload ) ); + } + + $this->send( + 'request.sent', + $payload + ); + + set_transient( $cache_key, true, 300 ); // 5 minute TTL. + } + + /** + * Fire the `app.transaction` event after a successful payment. + * + * @param string $reference Transaction reference (tx_ref). + * @param string $currency ISO 4217 currency code. + * @param float $amount Transaction amount. + * @param string $method Payment method (e.g. "card"). + * @param float $fee Transaction fee. + * @return void + */ + public function track_transaction( + string $reference, + string $currency, + float $amount, + string $method, + float $fee + ): void { + $this->app_id = $this->get_app_id(); + $this->environment = $this->get_current_environment(); + $this->send( + 'app.transaction', + array( + 'app_id' => $this->app_id, + 'reference' => $reference, + 'currency' => $currency, + 'amount' => $amount, + 'fee' => $fee, + 'method' => $method, + ) + ); + } + + /** + * Fire the `app.error` event when a payment fails. + * + * @param string $error_code Short machine-readable error code. + * @param string $error_message Human-readable error description. + * @return void + */ + public function track_error( string $error_code, string $error_message ): void { + $this->app_id = $this->get_app_id(); + $this->environment = $this->get_current_environment(); + $this->send( + 'app.error', + array( + 'app_id' => $this->app_id, + 'library' => self::LIBRARY, + 'library_version' => $this->library_version(), + 'error_code' => $error_code, + 'error_message' => $error_message, + ) + ); + } + + /** + * Dispatch an event to the SigNoz service. + * Uses non-blocking HTTP so the payment flow is never delayed. + * + * @param string $event_name SigNoz event name (e.g. "app.created"). + * @param array $data Event payload. + * @return void + */ + private function send( string $event_name, array $data ): void { + $payload = array( + 'name' => $event_name, + 'data' => $data, + 'timestamp' => gmdate( 'Y-m-d\TH:i:s.000\Z' ), + ); + + wp_remote_post( + self::BASE_URL . '/events', + array( + 'headers' => array( + 'Content-Type' => 'application/json', + 'x-api-key' => self::API_KEY, + ), + 'body' => wp_json_encode( $payload ), + 'blocking' => false, + 'timeout' => 5, + ) + ); + } + + /** + * Return the plugin version, falling back gracefully if the constant is not yet defined. + * + * @return string + */ + private function library_version(): string { + return defined( 'FLW_WC_VERSION' ) ? FLW_WC_VERSION : '3.1.0'; + } +} diff --git a/includes/views/html-admin-missing-woocommerce.php b/includes/views/html-admin-missing-woocommerce.php index 712b5ca..d86ec3e 100644 --- a/includes/views/html-admin-missing-woocommerce.php +++ b/includes/views/html-admin-missing-woocommerce.php @@ -12,7 +12,7 @@

' . esc_html__( 'Flutterwave WooCommerce', 'rave-woocommerce-payment-gateway' ) . '' ); + printf( esc_html__( '%s requires WooCommerce to be installed and activated in order to serve updates.', 'rave-woocommerce-payment-gateway' ), '' . esc_html__( 'Flutterwave WooCommerce', 'rave-woocommerce-payment-gateway' ) . '' ); ?>

diff --git a/package-lock.json b/package-lock.json index 90fc3c3..e2ee61f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rave-woocommerce-payment-gateway", - "version": "2.3.6", + "version": "3.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "rave-woocommerce-payment-gateway", - "version": "2.3.6", + "version": "3.2.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 56746e5..d86597b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "rave-woocommerce-payment-gateway", "title": "flutterwave-woocommerce", - "version": "3.1.0", + "version": "3.2.0", "description": "Official WooCommerce payment gateway for Flutterwave", "scripts": { "postinstall": "composer install", diff --git a/rave-woocommerce-payment-gateway.php b/rave-woocommerce-payment-gateway.php index a524463..6e877fb 100644 --- a/rave-woocommerce-payment-gateway.php +++ b/rave-woocommerce-payment-gateway.php @@ -3,7 +3,7 @@ * Plugin Name: Flutterwave WooCommerce * Plugin URI: https://developer.flutterwave.com/ * Description: Official WooCommerce payment gateway for Flutterwave. - * Version: 3.1.0 + * Version: 3.2.0 * Author: Flutterwave Developers * Author URI: http://flutterwave.com/us * License: MIT License @@ -62,7 +62,7 @@ function ( Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $payment add_action( 'before_woocommerce_init', - function() { + function () { if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true ); } @@ -82,14 +82,6 @@ function flw_plugin_action_links( array $links ): array { array_unshift( $links, "Settings" ); return $links; - } add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), 'flw_plugin_action_links' ); - - - - - - - diff --git a/readme.txt b/readme.txt index 1874664..a83f454 100644 --- a/readme.txt +++ b/readme.txt @@ -2,8 +2,8 @@ Contributors: theflutterwave Tags: fintech,flutterwave, woocommerce, payments, nigeria, mastercard, visa, target,Naira,payments,verve,donation,church,shop,store, ghana, kenya, international, mastercard, visa Requires at least: 3.1 -Tested up to: 6.7.1 -Stable tag: 3.1.0 +Tested up to: 7.0.0 +Stable tag: 3.2.0 License: MIT License URI: https://github.com/Flutterwave/Woocommerce/blob/master/LICENSE diff --git a/tests/PHPUnit/test-flutterwave-signoz-logger.php b/tests/PHPUnit/test-flutterwave-signoz-logger.php new file mode 100644 index 0000000..79764f4 --- /dev/null +++ b/tests/PHPUnit/test-flutterwave-signoz-logger.php @@ -0,0 +1,736 @@ +reset_singleton(); + $this->logger = Flutterwave_Signoz_Logger::instance(); + $this->captured_requests = []; + } + + public function tear_down(): void { + parent::tear_down(); + remove_all_filters( 'pre_http_request' ); + $this->reset_singleton(); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + /** + * Reset the singleton so each test starts with a fresh instance. + */ + private function reset_singleton(): void { + $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); + $prop = $ref->getProperty( 'instance' ); + $prop->setAccessible( true ); + $prop->setValue( null, null ); + } + + /** + * Read a private instance property via reflection. + * + * @param string $name Property name. + * @return mixed Property value. + * @throws ReflectionException If the property does not exist. + */ + private function get_property( string $name ) { + $ref = new ReflectionClass( Flutterwave_Signoz_Logger::class ); + $prop = $ref->getProperty( $name ); + $prop->setAccessible( true ); + return $prop->getValue( $this->logger ); + } + + /** + * Add a filter that stubs the merchant-info API response. + * + * @param string|null $merchant_name Value to return in `mn`; null omits the key. + * @param bool $wp_error Return a WP_Error instead of a response. + * @param string|null $raw_body Override the full response body (raw JSON string). + */ + private function mock_merchant_api( + ?string $merchant_name = null, + bool $wp_error = false, + ?string $raw_body = null + ): void { + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) use ( $merchant_name, $wp_error, $raw_body ) { + if ( strpos( $url, 'api.ravepay.co' ) === false ) { + return $response; + } + if ( $wp_error ) { + return new WP_Error( 'http_request_failed', 'Connection timed out' ); + } + if ( null !== $raw_body ) { + $body = $raw_body; + } elseif ( null !== $merchant_name ) { + $body = wp_json_encode( + [ + 'mn' => $merchant_name, + 'status' => 'success', + ] + ); + } else { + $body = wp_json_encode( [ 'status' => 'success' ] ); + } + return [ + 'body' => $body, + 'headers' => [], + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }, + 10, + 3 + ); + } + + /** + * Add a filter that captures every request sent to the SigNoz endpoint. + */ + private function capture_signoz_requests(): void { + add_filter( + 'pre_http_request', + function ( $response, $args, $url ) { + if ( strpos( $url, 'signozservice-prod' ) === false ) { + return $response; + } + $this->captured_requests[] = [ + 'url' => $url, + 'args' => $args, + 'body' => json_decode( $args['body'], true ), + ]; + return [ + 'body' => wp_json_encode( [ 'status' => 'ok' ] ), + 'headers' => [], + 'response' => [ + 'code' => 200, + 'message' => 'OK', + ], + ]; + }, + 10, + 3 + ); + } + + // ========================================================================= + // Singleton + // ========================================================================= + + public function test_instance_returns_flutterwave_signoz_logger(): void { + $this->assertInstanceOf( Flutterwave_Signoz_Logger::class, Flutterwave_Signoz_Logger::instance() ); + } + + public function test_instance_returns_same_object_on_repeated_calls(): void { + $a = Flutterwave_Signoz_Logger::instance(); + $b = Flutterwave_Signoz_Logger::instance(); + $this->assertSame( $a, $b ); + } + + // ========================================================================= + // get_merchant_id() + // ========================================================================= + + public function test_get_merchant_id_returns_name_from_mn_field(): void { + $this->mock_merchant_api( 'AcmeCorp' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertEquals( 'AcmeCorp', $result ); + } + + public function test_get_merchant_id_returns_null_on_wp_error(): void { + $this->mock_merchant_api( null, true ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-bad-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_when_mn_key_absent(): void { + $this->mock_merchant_api( null ); // response has no 'mn' key + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_on_malformed_json(): void { + $this->mock_merchant_api( null, false, 'not-valid-json' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_on_empty_body(): void { + $this->mock_merchant_api( null, false, '' ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + public function test_get_merchant_id_returns_null_when_mn_is_null_in_response(): void { + $this->mock_merchant_api( null, false, wp_json_encode( [ 'mn' => null ] ) ); + $result = $this->logger->get_merchant_id( 'FLWPUBK-test-key' ); + $this->assertNull( $result ); + } + + // ========================================================================= + // init() + // ========================================================================= + + public function test_init_sets_app_id_from_merchant_name(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->assertEquals( 'TestMerchant', $this->get_property( 'app_id' ) ); + } + + public function test_init_falls_back_to_public_key_when_merchant_id_is_null(): void { + $this->mock_merchant_api( null ); // no 'mn' in response + $this->logger->init( 'FLWPUBK-fallback-key', 'sandbox' ); + $this->assertEquals( 'FLWPUBK-fallback-key', $this->get_property( 'app_id' ) ); + } + + public function test_init_falls_back_to_public_key_on_wp_error(): void { + $this->mock_merchant_api( null, true ); + $this->logger->init( 'FLWPUBK-network-fail', 'sandbox' ); + $this->assertEquals( 'FLWPUBK-network-fail', $this->get_property( 'app_id' ) ); + } + + public function test_init_sets_environment_to_sandbox(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); + } + + public function test_init_sets_environment_to_production(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->assertEquals( 'production', $this->get_property( 'environment' ) ); + } + + public function test_init_defaults_environment_to_sandbox(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->assertEquals( 'sandbox', $this->get_property( 'environment' ) ); + } + + public function test_app_id_is_empty_before_init(): void { + $this->assertEquals( '', $this->get_property( 'app_id' ) ); + } + + // ========================================================================= + // track_app_created() + // ========================================================================= + + public function test_track_app_created_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_app_created_sends_exactly_one_request(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $this->assertCount( 1, $this->captured_requests ); + } + + public function test_track_app_created_sends_correct_event_name(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $this->assertEquals( 'app.created', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_app_created_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'TestMerchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_app_created( 'FLWPUBK-test-key' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'TestMerchant', $data['app_id'] ); + $this->assertNull( $data['client_id'] ); + $this->assertEquals( 'FLWPUBK-test-key', $data['public_key'] ); + $this->assertEquals( 'woocommerce', $data['library'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + // ========================================================================= + // track_request_sent() + // ========================================================================= + + public function test_track_request_sent_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_request_sent_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + + $this->assertEquals( 'request.sent', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_request_sent_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'production' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'card', 'txn-ref-123', '/v3/charges' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'production', $data['environment'] ); + $this->assertEquals( 'v3', $data['api_version'] ); + $this->assertEquals( 'card', $data['method'] ); + $this->assertEquals( 'txn-ref-123', $data['reference'] ); + $this->assertEquals( '/v3/charges', $data['path'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + public function test_track_request_sent_includes_sandbox_environment(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key', 'sandbox' ); + $this->capture_signoz_requests(); + + $this->logger->track_request_sent( 'banktransfer', 'ref-456', '/v3/transfers' ); + + $this->assertEquals( 'sandbox', $this->captured_requests[0]['body']['data']['environment'] ); + } + + // ========================================================================= + // track_transaction() + // ========================================================================= + + public function test_track_transaction_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_transaction_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + + $this->assertEquals( 'app.transaction', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_transaction_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-123', 'NGN', 5000.00, 'card', 70.00 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'ref-123', $data['reference'] ); + $this->assertEquals( 'NGN', $data['currency'] ); + $this->assertEquals( 5000.00, $data['amount'] ); + $this->assertEquals( 70.00, $data['fee'] ); + $this->assertEquals( 'card', $data['method'] ); + } + + public function test_track_transaction_with_zero_amount_and_fee(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-zero', 'USD', 0.0, 'card', 0.0 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 0.0, $data['amount'] ); + $this->assertEquals( 0.0, $data['fee'] ); + } + + public function test_track_transaction_with_fractional_amounts(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_transaction( 'ref-frac', 'GHS', 99.99, 'mobilemoney', 1.50 ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 99.99, $data['amount'] ); + $this->assertEquals( 1.50, $data['fee'] ); + } + + public function test_track_transaction_supports_different_currencies(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + foreach ( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ] as $currency ) { + $this->logger->track_transaction( 'ref-' . $currency, $currency, 100.0, 'card', 1.0 ); + } + + $this->assertCount( 5, $this->captured_requests ); + $sent_currencies = array_column( + array_column( array_column( $this->captured_requests, 'body' ), 'data' ), + 'currency' + ); + $this->assertEquals( [ 'NGN', 'USD', 'GBP', 'KES', 'ZAR' ], $sent_currencies ); + } + + // ========================================================================= + // track_error() + // ========================================================================= + + public function test_track_error_does_not_send_before_init(): void { + $this->capture_signoz_requests(); + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined' ); + $this->assertEmpty( $this->captured_requests ); + } + + public function test_track_error_sends_correct_event_name(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); + + $this->assertEquals( 'app.error', $this->captured_requests[0]['body']['name'] ); + } + + public function test_track_error_payload_contains_required_fields(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( 'PAYMENT_FAILED', 'Card declined by issuer' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( 'Merchant', $data['app_id'] ); + $this->assertEquals( 'woocommerce', $data['library'] ); + $this->assertEquals( 'PAYMENT_FAILED', $data['error_code'] ); + $this->assertEquals( 'Card declined by issuer', $data['error_message'] ); + $this->assertArrayHasKey( 'library_version', $data ); + } + + public function test_track_error_with_empty_strings(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $this->logger->track_error( '', '' ); + + $data = $this->captured_requests[0]['body']['data']; + $this->assertEquals( '', $data['error_code'] ); + $this->assertEquals( '', $data['error_message'] ); + } + + public function test_track_error_with_special_characters(): void { + $this->mock_merchant_api( 'Merchant' ); + $this->logger->init( 'FLWPUBK-test-key' ); + $this->capture_signoz_requests(); + + $code = 'ERR_