├── amazon-payments-advanced.php ├── blocks-metadata ├── change-address │ └── block.json └── log-out-banner │ └── block.json ├── changelog.txt ├── docs ├── USER.md └── images │ ├── amazon-meta-box.png │ ├── amazon-payments-sign-in.png │ ├── checkout-page.png │ ├── checkout-settings.png │ ├── hide-shipping-costs.png │ ├── sign-in-from-cart-page.png │ ├── sign-in-from-checkout-page.png │ ├── subscription-notice.png │ └── use-amazon-login-app.png ├── includes ├── admin │ ├── class-wc-amazon-payments-advanced-admin.php │ ├── class-wc-amazon-payments-advanced-order-admin.php │ └── legacy │ │ └── class-wc-amazon-payments-advanced-order-admin-legacy.php ├── blocks │ └── class-wc-amazon-payments-advanced-register-blocks.php ├── class-wc-amazon-payments-advanced-alexa-notifications.php ├── class-wc-amazon-payments-advanced-api-abstract.php ├── class-wc-amazon-payments-advanced-api.php ├── class-wc-amazon-payments-advanced-compat.php ├── class-wc-amazon-payments-advanced-install.php ├── class-wc-amazon-payments-advanced-ipn-handler-abstract.php ├── class-wc-amazon-payments-advanced-ipn-handler.php ├── class-wc-amazon-payments-advanced-merchant-onboarding-handler.php ├── class-wc-amazon-payments-advanced-rest-api-controller.php ├── class-wc-amazon-payments-advanced-utils.php ├── class-wc-gateway-amazon-payments-advanced-abstract.php ├── class-wc-gateway-amazon-payments-advanced-express.php ├── class-wc-gateway-amazon-payments-advanced-privacy.php ├── class-wc-gateway-amazon-payments-advanced-subscriptions.php ├── class-wc-gateway-amazon-payments-advanced.php ├── compats │ ├── class-wc-amazon-payments-advanced-drip-compat.php │ ├── class-wc-amazon-payments-advanced-dynamic-pricing-compat.php │ ├── class-wc-amazon-payments-advanced-multi-currency-abstract.php │ ├── class-wc-amazon-payments-advanced-multi-currency-ppbc.php │ ├── class-wc-amazon-payments-advanced-multi-currency-wccw.php │ ├── class-wc-amazon-payments-advanced-multi-currency-woocs.php │ ├── class-wc-amazon-payments-advanced-multi-currency-wpml.php │ ├── class-wc-amazon-payments-advanced-multi-currency.php │ ├── class-wc-amazon-payments-advanced-subscribe-to-newsletter-compat.php │ ├── class-wc-amazon-payments-advanced-wgm-compat.php │ ├── class-wc-amazon-payments-advanced-woocommerce-multilingual-compat.php │ └── woo-blocks │ │ ├── class-wc-amazon-payments-advanced-block-compat-abstract.php │ │ ├── class-wc-amazon-payments-advanced-block-compat-classic.php │ │ ├── class-wc-amazon-payments-advanced-block-compat-express.php │ │ └── class-wc-amazon-payments-advanced-block-compatibility.php └── legacy │ ├── class-wc-amazon-payments-advanced-api-legacy.php │ ├── class-wc-amazon-payments-advanced-ipn-handler-legacy.php │ ├── class-wc-gateway-amazon-payments-advanced-legacy.php │ └── class-wc-gateway-amazon-payments-advanced-subscriptions-legacy.php ├── phpstan.neon ├── readme.txt ├── templates └── emails │ └── legacy │ ├── hard-decline.php │ └── soft-decline.php └── woocommerce-gateway-amazon-payments-advanced.php /amazon-payments-advanced.php: -------------------------------------------------------------------------------- 1 | $active_plugin ) { 15 | if ( strstr( $active_plugin, '/amazon-payments-advanced.php' ) ) { 16 | $active_plugins[ $key ] = str_replace( '/amazon-payments-advanced.php', '/woocommerce-gateway-amazon-payments-advanced.php', $active_plugin ); 17 | } 18 | } 19 | update_option( 'active_plugins', $active_plugins ); 20 | -------------------------------------------------------------------------------- /blocks-metadata/change-address/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 2, 4 | "name": "amazon-payments-advanced/change-address", 5 | "title": "Amazon Change Address Block Widget", 6 | "category": "woocommerce", 7 | "parent": ["woocommerce/checkout-shipping-address-block"], 8 | "textdomain": "woocommerce-gateway-amazon-payments-advanced", 9 | "attributes": { 10 | "lock": { 11 | "type": "object", 12 | "default": { 13 | "remove": true, 14 | "move": true 15 | } 16 | } 17 | }, 18 | "supports": { 19 | "html": false 20 | }, 21 | "editorScript": "amazon-payments-advanced-blocks-change-address-editor" 22 | } 23 | -------------------------------------------------------------------------------- /blocks-metadata/log-out-banner/block.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schemas.wp.org/trunk/block.json", 3 | "apiVersion": 2, 4 | "name": "amazon-payments-advanced/log-out-banner", 5 | "title": "Amazon Log Out Banner", 6 | "category": "woocommerce", 7 | "parent": ["woocommerce/checkout-fields-block"], 8 | "textdomain": "woocommerce-gateway-amazon-payments-advanced", 9 | "attributes": { 10 | "lock": { 11 | "type": "object", 12 | "default": { 13 | "remove": true, 14 | "move": true 15 | } 16 | } 17 | }, 18 | "supports": { 19 | "html": false 20 | }, 21 | "editorScript": "amazon-payments-advanced-blocks-log-out-banner-editor", 22 | "style": "amazon-payments-advanced-blocks-log-out-banner" 23 | } 24 | -------------------------------------------------------------------------------- /docs/USER.md: -------------------------------------------------------------------------------- 1 | Amazon Pay User Documentation 2 | ============================= 3 | 4 | [Amazon Pay](http://woocommerce.com/products/pay-with-amazon/?_ga=1.37349536.1089344366.1467581921) extends WooCommerce, allowing you to take payments via a special checkout page in your store powered by Amazon widgets. This offers a convenient way for Amazon users to pay using existing payment and address details. 5 | 6 | This extension works for stores based in US, Europe, and Japan. Recurring payments with WooCommerce Subscriptions is supported, except for Germany and UK in which merchants need to [contact Amazon](https://payments.amazon.com/contact) and enable it via filter. 7 | 8 | > **Note**: An [SSL certificate](http://docs.woocommerce.com/document/ssl-and-https/) is required for Amazon Pay. Please contact your hosting provider to purchase and install on your site. 9 | 10 | ## Table of Contents 11 | 12 | * [Installation](#installation) 13 | * [Sign up with Amazon](#sign-up-with-amazon) 14 | * [Setup and Configuration](#setup-and-configuration) 15 | * [Selling Free Items with Shipping](#selling-free-items-with-shipping) 16 | * [Customer Checkout Flow](#customer-checkout-flow) 17 | * [Cart sign-in widget](#cart-sign-in-widget) 18 | * [Checkout sign-in widget](#checkout-sign-in-widget) 19 | * [Signing in](#signing-in) 20 | * [Amazon Checkout Page](#amazon-checkout-page) 21 | * [Admin Order Fulfillment Flow](#admin-order-fulfillment-flow) 22 | * [REST API](#rest-api) 23 | * [List of orders paid via Amazon Pay](#list-of-orders-paid-via-amazon-payments) 24 | * [Authorize the order](#authorize-the-order) 25 | * [Close authorization](#close-authorization) 26 | * [Capture the order](#capture-the-order) 27 | * [Authorize and capture the order](#authorize-and-capture-the-order) 28 | * [Refund the order](#refund-the-order) 29 | * [FAQ](#faq) 30 | * [Will this gateway work with X extension that modifies the WooCommerce checkout page?](#will-this-gateway-work-with-x-extension-that-modifies-the-woocommerce-checkout-page) 31 | * [Can I use Pay with WooCommerce Subscriptions?](#can-i-use-pay-with-amazonamazon-payments-advanced-with-woocommerce-subscriptions) 32 | * [Where should I go to view my Amazon account balance?](#where-should-i-go-to-view-my-amazon-account-balance) 33 | * [My Amazon Pay widgets look stretched or distorted on my mobile device. How do I resolve this?](#my-amazon-payments-widgets-look-stretched-or-distorted-on-my-mobile-device-how-do-i-resolve-this) 34 | * [Troubleshooting](#troubleshooting) 35 | * [Shipping costs are missing](#shipping-costs-are-missing) 36 | 37 | ## Installation 38 | 39 | 1. **Download** the .zip file from your [WooCommerce.com account](https://woocommerce.com/my-account/downloads/?_ga=1.206734355.1089344366.1467581921). 40 | 1. **Go to: WordPress Admin > Plugins > Add New** to upload the file you downloaded with Choose File. 41 | 1. **Activate** the extension. 42 | 43 | More information at: [Installing and Managing Plugins](http://codex.wordpress.org/Managing_Plugins#Installing_Plugins). 44 | 45 | ## Sign up with Amazon 46 | 47 | You need to register for a Pay with Amazon Merchant account at the following link(s), depending on your location: 48 | 49 | * [US – Pay with Amazon](http://go.amazonservices.com/WooCommerce25.html) 50 | * [UK – Pay with Amazon](https://payments.amazon.co.uk/business/pre-registration-api?ld=SPEXUKAPAWoocommerce) 51 | * [DE – Bezhalen über Amazon](https://payments.amazon.de/business/pre-registration-api?ld=SPEXUKAPAWoocommerce) 52 | * [JP – Amazonペイメント](https://payments.amazon.co.jp/home) 53 | 54 | ## Setup and Configuration 55 | 56 | ![Amazon Pay checkout settings](images/checkout-settings.png) 57 | 58 | 1. **Go to: WooCommerce > Settings > Checkout > Amazon Pay**. 59 | 1. **Tick** the box to Enable. 60 | 1. **Enter** Merchant account details provided by Amazon after signup: 61 | 1. **Seller ID** 62 | 1. **MWS Access Key** 63 | 1. **Secret Key** 64 | 1. **Use Login App** — Tick the box if you signed up for this option. Complete the App Client ID and App Client Secret fields that appear. 65 | 1. **Tick** the **Use Sandbox** box for testing only. *No live payments are taken*. 66 | 1. **Payment Capture** – From the dropdown, select how payments are handled. 67 | 1. **Authorize and capture** — After placing the order, payment is automatically captured. 68 | 1. **Authorize** — After placing the order, the payment is automatically authorized, but you need to manually capture later. 69 | 1. **Don’t authorize** — After placing the order, you need to manually capture and authorize payment. Useful for pre-orders. 70 | 1. **Select** button or banner from the dropdown for **Cart login display**. 71 | 1. **Tick** the box to hide standard checkout button (optional). 72 | 1. **Tick** the Debug box if you wish to log errors. This is helpful for troubleshooting. 73 | 74 | Retrieve your account details by logging into Amazon’s Seller Central. Your Seller ID is located under **Settings > Integration Settings** and your MWS Access and Secret Keys under **Integration > MWS Access Key**. Seller Central is located at: 75 | 76 | * US: https://sellercentral.amazon.com 77 | * EU: https://sellercentral-europe.amazon.com 78 | * JP: https://sellercentral-japan.amazon.com/ 79 | 80 | Once configured, the plugin is ready to use. 81 | 82 | ## Selling Free Items with Shipping 83 | 84 | If you’re selling zero-priced products in which you need to charge shipping: 85 | 86 | 1. **Go to: WooCommerce > Settings > Checkout > Pay with Amazon**. 87 | 1. **Tick** the checkbox for **Use Amazon Login App**. 88 | 1. **Save changes**. 89 | 90 | ![Use Amazon Login App](images/use-amazon-login-app.png) 91 | 92 | ## Customer Checkout Flow 93 | 94 | To pay using Pay with Amazon, customers need to first sign in with their Amazon account. They can do this from two places. 95 | 96 | ### Cart sign-in widget 97 | 98 | On the cart page, customers see a Amazon Pay button next to the regular checkout button: 99 | 100 | ![Cart sign-in widget](images/sign-in-from-cart-page.png) 101 | 102 | ### Checkout sign-in widget 103 | 104 | On the regular checkout page, customers see a banner asking if they want to use Amazon Pay: 105 | 106 | ![Checkout sign-in widget](images/sign-in-from-checkout-page.png) 107 | 108 | ### Signing in 109 | 110 | Both buttons trigger a sign-in window that opens and looks similar to this: 111 | 112 | ![Login with Amazon Sign-in window](images/amazon-payments-sign-in.png) 113 | 114 | After signing in, the customer is redirected to a special version of the checkout that shows Amazon widgets. 115 | 116 | ### Amazon Checkout Page 117 | 118 | Upon reaching the checkout page, note that the regular billing and shipping forms are missing. This is normal — they have been replaced by Amazon widgets: 119 | 120 | ![Checkout page](images/checkout-page.png) 121 | 122 | Customers choose their desired shipping address and payment method, and this refreshes the WooCommerce order totals as normal. 123 | 124 | They then click the ‘Place Order’ button. This create an order in WooCommerce for you to process. 125 | 126 | ## Admin Order Fulfillment Flow 127 | 128 | When an order comes in via Pay with Amazon, it is placed On Hold. If you view/edit the order, a new meta box appears on your Orders screen: 129 | 130 | ![Amzon meta box](images/amazon-meta-box.png) 131 | 132 | This widget allows you to perform the following actions: 133 | 134 | * **Authorize** — Authorize a payment but do not capture yet. Amazon recommends capturing payment once you have verified the order and have begun processing it. 135 | * **Authorize and Capture** — Capture the full order total amount. 136 | * **Capture** — Capture an authorized payment. Amazon recommends capturing payment once you have fulfilled the order. 137 | * **Close authorization** — Close an authorization and release funds without capturing. 138 | * **Refund** — If you captured a payment that is still open, you can refund X amount. 139 | 140 | After each action, an order note is created, noting the status. 141 | 142 | To view your Amazon Pay merchant account balance, visit: 143 | 144 | * US: https://sellercentral.amazon.com 145 | * EU: https://sellercentral-europe.amazon.com 146 | * JP: https://sellercentral-japan.amazon.com 147 | 148 | ## REST API 149 | 150 | Since 1.6.0, Pay with Amazon exposes some functionalities through REST API. 151 | 152 | The Pay with Amazon REST API allows you to authorize, capture, and close authorization. 153 | The endpoint is `/wp-json/wc/v1/orders//amazon-payments-advanced/`. 154 | 155 | ### List of orders paid via Amazon Pay 156 | 157 | There's no custom endpoint to retrieve list of orders paid via Amazon Pay. The built-in orders point can be used with 158 | `_payment_method=amazon_payments_advanced` filter. 159 | 160 | ``` 161 | GET /wp-json/wc/v1/orders?filter[_payment_method]=amazon_payments_advanced 162 | ``` 163 | 164 | ``` 165 | curl -g -X GET 'https://example.com/wp-json/wc/v1/orders?filter[_payment_method]=amazon_payments_advanced' -u consumer_key:consumer_secret 166 | ``` 167 | 168 | For CURL request that involves filter query (`[]`), you need to specify `-g` (to turn off [URL globbing](http://ec.haxx.se/cmdline-globbing.html)). 169 | 170 | JSON response example: 171 | 172 | ``` 173 | [ 174 | { 175 | "id": 132, 176 | "status": "on-hold", 177 | "order_key": "wc_order_57bb41b6eeb32", 178 | "number": 4606, 179 | "currency": "GBP", 180 | ... 181 | "amazon_reference": { 182 | "amazon_reference_state": "Open", 183 | "amazon_reference_id": "S02-0312204-2022855", 184 | "amazon_authorization_state": "", 185 | "amazon_authorization_id": "", 186 | "amazon_capture_state": "", 187 | "amazon_capture_id": "", 188 | "amazon_refund_ids": [] 189 | }, 190 | ... 191 | }, 192 | ... 193 | ] 194 | ``` 195 | 196 | Orders paid via Amazon Pay will have `amazon_reference` on order item. 197 | 198 | The `filter` parameter can be used with `status` parameter to retrieve list of orders that have been authorized but not captured yet. 199 | 200 | ``` 201 | curl -g -X GET 'https://example.com/wp-json/wc/v1/orders?filter[_payment_method]=amazon_payments_advanced&filter[amazon_authorization_state]=Open&status=on-hold' -u consumer_key:consumer_secret 202 | ``` 203 | 204 | ### Authorize the order 205 | 206 | ``` 207 | POST /wp-json/wc/v1/orders//amazon-payments-advanced/authorize 208 | ``` 209 | 210 | ``` 211 | curl -X GET 'https://example.com/wp-json/wc/v1/orders/123/amazon-payments-advanced/authorize ' -u consumer_key:consumer_secret 212 | ``` 213 | 214 | JSON response example: 215 | 216 | ``` 217 | { 218 | "authorized": true, 219 | "amazon_authorization_id": "S02-6972444-9928455-A066187" 220 | } 221 | ``` 222 | 223 | Possible JSON response with error: 224 | 225 | ``` 226 | { 227 | "code": "TransactionAmountExceeded", 228 | "message": "OrderReference S02-6972444-9928455 has already been authorized for amount 21.85 GBP. A new Authorization with amount 21.85 GBP cannot be accepted as the total Authorization amount cannot exceed 25.13 GBP.", 229 | "data": null 230 | } 231 | ``` 232 | 233 | ``` 234 | { 235 | "code": "woocommerce_rest_order_invalid_id", 236 | "message": "Invalid order ID.", 237 | "data": { 238 | "status": 404 239 | } 240 | } 241 | ``` 242 | 243 | ### Close authorization 244 | 245 | ``` 246 | POST /wp-json/wc/v1/orders//amazon-payments-advanced/close-authorization 247 | ``` 248 | 249 | ``` 250 | curl -X GET 'https://example.com/wp-json/wc/v1/orders/123/amazon-payments-advanced/close-authorization ' -u consumer_key:consumer_secret 251 | ``` 252 | 253 | JSON response example: 254 | 255 | ``` 256 | { 257 | "authorization_closed": true 258 | } 259 | ``` 260 | 261 | Possible JSON response with error: 262 | 263 | ``` 264 | { 265 | "code": "woocommerce_rest_order_missing_amazon_authorization_id", 266 | "message": "Specified resource does not have Amazon authorization ID", 267 | "data": { 268 | "status": 400 269 | } 270 | } 271 | ``` 272 | 273 | ### Capture the order 274 | 275 | ``` 276 | POST /wp-json/wc/v1/orders//amazon-payments-advanced/capture 277 | ``` 278 | 279 | ``` 280 | curl -X GET 'https://example.com/wp-json/wc/v1/orders/123/amazon-payments-advanced/capture ' -u consumer_key:consumer_secret 281 | ``` 282 | 283 | JSON response example: 284 | 285 | ``` 286 | { 287 | "captured": true, 288 | "amazon_capture_id": "S02-6972444-9928455-C066187" 289 | } 290 | ``` 291 | 292 | Possible JSON response with error: 293 | 294 | ``` 295 | { 296 | "code": "InvalidAuthorizationStatus", 297 | "message": "Authorization S02-6972444-9928455-A066187 is currently in Closed state. Capture can only be requested in Open state.", 298 | "data": null 299 | } 300 | ``` 301 | 302 | ### Authorize and capture the order 303 | 304 | ``` 305 | POST /wp-json/wc/v1/orders//amazon-payments-advanced/authorize-and-capture 306 | ``` 307 | 308 | ``` 309 | curl -X GET 'https://example.com/wp-json/wc/v1/orders/123/amazon-payments-advanced/authorize-and-capture ' -u consumer_key:consumer_secret 310 | ``` 311 | 312 | JSON response example: 313 | 314 | ``` 315 | { 316 | "authorized": true, 317 | "amazon_authorization_id": "S02-4966596-9591203-A079366", 318 | "captured": true, 319 | "amazon_capture_id": "S02-4966596-9591203-C079366" 320 | } 321 | ``` 322 | 323 | Possible JSON response with error: 324 | 325 | ``` 326 | { 327 | "code": "InvalidAuthorizationStatus", 328 | "message": "Authorization S02-6972444-9928455-A066187 is currently in Closed state. Capture can only be requested in Open state.", 329 | "data": null 330 | } 331 | ``` 332 | 333 | ### Refund the order 334 | 335 | ``` 336 | POST /wp-json/wc/v1/orders//amazon-payments-advanced/refund 337 | ``` 338 | 339 | ``` 340 | curl -X GET 'https://example.com/wp-json/wc/v1/orders/123/amazon-payments-advanced/refund' \ 341 | -u consumer_key:consumer_secret \ 342 | -H 'Content-Type: application/json' \ 343 | -d '{"amount": "20.00", "reason": "reason for refund"}' 344 | ``` 345 | 346 | JSON response example: 347 | 348 | ``` 349 | { 350 | "refunded": true, 351 | "amazon_refund_id": "S02-1228806-5112466-R043423" 352 | } 353 | ``` 354 | 355 | ## FAQ 356 | 357 | ### Will this gateway work with X extension that modifies the WooCommerce checkout page? 358 | 359 | Generally, no. Amazon Pay uses its own forms for payment and shipping, so plugins that add or modify WC checkout may not work in these cases. Since 1.6.0, compatibilities with extensions that modify checkout page has been added. Those are extensions are: 360 | 361 | * [WooCommerce Drip](https://woocommerce.com/products/woocommerce-drip/) 362 | * [WooCommerce Dynamic Pricing](https://woocommerce.com/products/dynamic-pricing/) 363 | * WooCommerce German Market 364 | 365 | ### Can I use Pay with Amazon Pay with WooCommerce Subscriptions? 366 | 367 | Yes, this Amazon Pay integration supports recurring payments with [WooCommerce Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/?_ga=1.101887745.1089344366.1467581921) (separate purchase). More info at: [Subscription Payment Methods & Gateways](https://docs.woocommerce.com/document/subscriptions/payment-gateways/). 368 | 369 | ### Where should I go to view my Amazon account balance? 370 | 371 | To view your Amazon Pay merchant account balance, visit http://sellercentral.amazon.com for the US; or https://sellercentral-europe.amazon.com) for Europe; or https://sellercentral-japan.amazon.com/ for Japan. Not payments.amazon.com, payments.amazon.co.uk or payments.amazon.de or payments.amazon.co.jp 372 | 373 | ### My Amazon Pay widgets look stretched or distorted on my mobile device. How do I resolve this? 374 | 375 | Stretching or distortion of Amazon Pay widgets when viewing your website on a mobile device is largely due to the WordPress theme you’re using. It may add padding around the widgets, causing the irregular display. 376 | 377 | To fix, add the following CSS code to “style.css” in your child theme; or the “style.css” file in your parent theme if you’re not yet using a child theme: 378 | 379 | ``` 380 | #amazon_addressbook_widget div, #amazon_wallet_widget div { 381 | min-width: 260px !important; 382 | 383 | } 384 | ``` 385 | 386 | ## Troubleshooting 387 | 388 | ### Shipping costs are missing 389 | 390 | This can occur if the following option is enabled. Disabling should solve the issue: 391 | 392 | ![Hide shipping costs](images/hide-shipping-costs.png) 393 | -------------------------------------------------------------------------------- /docs/images/amazon-meta-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/amazon-meta-box.png -------------------------------------------------------------------------------- /docs/images/amazon-payments-sign-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/amazon-payments-sign-in.png -------------------------------------------------------------------------------- /docs/images/checkout-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/checkout-page.png -------------------------------------------------------------------------------- /docs/images/checkout-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/checkout-settings.png -------------------------------------------------------------------------------- /docs/images/hide-shipping-costs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/hide-shipping-costs.png -------------------------------------------------------------------------------- /docs/images/sign-in-from-cart-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/sign-in-from-cart-page.png -------------------------------------------------------------------------------- /docs/images/sign-in-from-checkout-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/sign-in-from-checkout-page.png -------------------------------------------------------------------------------- /docs/images/subscription-notice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/subscription-notice.png -------------------------------------------------------------------------------- /docs/images/use-amazon-login-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/woocommerce/woocommerce-gateway-amazon-pay/9f01e6e3aa163e810eae15629041b16315a16840/docs/images/use-amazon-login-app.png -------------------------------------------------------------------------------- /includes/admin/class-wc-amazon-payments-advanced-order-admin.php: -------------------------------------------------------------------------------- 1 | id !== $screen_to_check ) { 48 | return; 49 | } 50 | 51 | if ( ! isset( $_GET['amazon_action'] ) ) { 52 | return; 53 | } 54 | 55 | check_admin_referer( 'amazon_order_action', 'security' ); 56 | 57 | // Find order id with HPOS disabled. 58 | $order_id = ! empty( $_GET['post'] ) ? (int) $_GET['post'] : false; 59 | if ( ! $order_id ) { 60 | // Find order id with HPOS enabled. 61 | $order_id = ! empty( $_GET['id'] ) ? (int) $_GET['id'] : false; 62 | } 63 | 64 | $order_id = absint( $order_id ); 65 | if ( ! $order_id ) { 66 | return; 67 | } 68 | 69 | $order = wc_get_order( $order_id ); 70 | if ( ! ( $order instanceof \WC_Order ) ) { 71 | return; 72 | } 73 | 74 | $version = WC_Amazon_Payments_Advanced::get_order_version( $order_id ); 75 | 76 | $id = isset( $_GET['amazon_id'] ) ? wc_clean( $_GET['amazon_id'] ) : ''; 77 | $action = sanitize_title( $_GET['amazon_action'] ); 78 | 79 | do_action( 'wc_amazon_do_order_action', $order, $id, $action, $version ); 80 | 81 | wp_safe_redirect( remove_query_arg( array( 'amazon_action', 'amazon_id', 'security' ) ) ); 82 | exit; 83 | } 84 | 85 | /** 86 | * Do the order action 87 | * 88 | * @param WC_Order $order Order object. 89 | * @param string $id A charge_id, refund_id or charge_permission_id. 90 | * @param string $action Action to be performed. 91 | * @param string $version Version of the order. 92 | */ 93 | public function do_order_action( $order, $id, $action, $version ) { 94 | if ( 'v2' !== strtolower( $version ) ) { 95 | return; 96 | } 97 | $order_id = $order->get_id(); 98 | wc_apa()->log( sprintf( 'Info: Trying to perform "%s" for order #%s', $action, $order_id ) ); 99 | switch ( $action ) { 100 | case 'refresh': 101 | wc_apa()->get_gateway()->log_charge_permission_status_change( $order ); 102 | wc_apa()->get_gateway()->log_charge_status_change( $order ); 103 | break; 104 | case 'authorize': 105 | case 'authorize_capture': 106 | $capture_now = ( 'authorize_capture' === $action ); 107 | wc_apa()->get_gateway()->perform_authorization( $order, $capture_now, $id ); 108 | break; 109 | case 'close_authorization': 110 | wc_apa()->get_gateway()->perform_cancel_auth( $order, $id ); 111 | break; 112 | case 'capture': 113 | wc_apa()->get_gateway()->perform_capture( $order, $id ); 114 | break; 115 | case 'refund': 116 | wc_apa()->get_gateway()->perform_refund( $order, null, $id ); 117 | break; 118 | } 119 | } 120 | 121 | /** 122 | * Amazon Pay authorization metabox. 123 | * 124 | * @param Object $screen The current screen object. 125 | * @param WC_Order|WP_Post $order The current post/order object. 126 | * @return void 127 | */ 128 | public function meta_box( $screen, $order ) { 129 | 130 | $order = $order instanceof \WC_Order ? $order : wc_get_order( $order->ID ); 131 | if ( ! ( $order instanceof \WC_Order ) ) { 132 | return; 133 | } 134 | 135 | if ( 'amazon_payments_advanced' !== wc_apa_get_order_prop( $order, 'payment_method' ) ) { 136 | return; 137 | } 138 | 139 | $screen = WC_Amazon_Payments_Advanced_Utils::get_edit_order_screen_id(); 140 | 141 | $post_types = apply_filters( 'woocommerce_amazon_pa_admin_meta_box_post_types', array( $screen ) ); 142 | 143 | foreach ( $post_types as $post_type ) { 144 | add_meta_box( 145 | 'woocommerce-amazon-payments-advanced', 146 | __( 'Amazon Pay', 'woocommerce-gateway-amazon-payments-advanced' ), 147 | array( $this, 'authorization_box' ), 148 | $post_type, 149 | 'side' 150 | ); 151 | } 152 | } 153 | 154 | /** 155 | * Authorization metabox content. 156 | * 157 | * @param WC_Order|WP_Post $object The current post/order object. 158 | * @return void 159 | */ 160 | public function authorization_box( $object ) { 161 | $order = $object instanceof WC_Order ? $object : wc_get_order( $object->ID ); 162 | 163 | $version = WC_Amazon_Payments_Advanced::get_order_version( $order->get_id() ); 164 | 165 | do_action( 'wc_amazon_authorization_box_render', $order, $version ); 166 | } 167 | 168 | /** 169 | * Turn a status object into a string for a label 170 | * 171 | * @param object $status_details Status details object. 172 | * @return string 173 | */ 174 | private function status_details_label( $status_details ) { 175 | $charge_status_full = $status_details->status; 176 | if ( ! empty( $status_details->reasons ) ) { 177 | $charge_status_full .= sprintf( ' (%1$s)', implode( ', ', wp_list_pluck( $status_details->reasons, 'reasonCode' ) ) ); 178 | } 179 | 180 | return $charge_status_full; 181 | } 182 | 183 | /** 184 | * Render the Admin meta box 185 | * 186 | * @param WC_Order $order Order object. 187 | * @param string $version Version of the order. 188 | */ 189 | public function auth_box_render( $order, $version ) { 190 | if ( 'v2' !== strtolower( $version ) ) { 191 | return; 192 | } 193 | 194 | $actions = array( 195 | 'refresh' => array( 196 | 'id' => $order->get_id(), 197 | 'button' => __( 'Refresh', 'woocommerce-gateway-amazon-payments-advanced' ), 198 | ), 199 | ); 200 | 201 | $need_refresh = false; 202 | 203 | $charge_permission_id = WC_Amazon_Payments_Advanced::get_order_charge_permission( $order->get_id() ); 204 | 205 | $charge_permission_cached_status = wc_apa()->get_gateway()->get_cached_charge_permission_status( $order ); 206 | 207 | $charge_permission_status_label = ! is_wp_error( $charge_permission_cached_status ) ? $this->status_details_label( $charge_permission_cached_status ) : false; 208 | 209 | $charge_permission_status_label = $charge_permission_status_label ? $charge_permission_status_label : __( 'Invalid', 'woocommerce-gateway-amazon-payments-advanced' ); 210 | /* translators: 1) Charge Permission ID 2) Status. */ 211 | echo wp_kses_post( wpautop( sprintf( __( 'Charge Permission %1$s is %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), esc_html( $charge_permission_id ), esc_html( $charge_permission_status_label ) ) ) ); 212 | 213 | $charge_permission_status = ! is_wp_error( $charge_permission_cached_status ) ? $charge_permission_cached_status->status : ''; 214 | 215 | switch ( $charge_permission_status ) { 216 | case 'Chargeable': 217 | $actions['authorize'] = array( 218 | 'id' => $charge_permission_id, 219 | 'button' => __( 'Authorize', 'woocommerce-gateway-amazon-payments-advanced' ), 220 | ); 221 | 222 | $actions['authorize_capture'] = array( 223 | 'id' => $charge_permission_id, 224 | 'button' => __( 'Authorize & Capture', 'woocommerce-gateway-amazon-payments-advanced' ), 225 | ); 226 | $need_refresh = true; 227 | break; 228 | case 'Closed': 229 | break; 230 | case 'NonChargeable': 231 | $need_refresh = true; 232 | break; 233 | default: 234 | // TODO: This is an unknown state, maybe handle? 235 | break; 236 | } 237 | 238 | $charge_id = WC_Amazon_Payments_Advanced::get_order_charge_id( $order->get_id() ); 239 | 240 | if ( ! empty( $charge_id ) ) { 241 | $charge_cached_status = wc_apa()->get_gateway()->get_cached_charge_status( $order ); 242 | 243 | $charge_status_label = $this->status_details_label( $charge_cached_status ); 244 | $charge_status_label = $charge_status_label ? $charge_status_label : __( 'Invalid', 'woocommerce-gateway-amazon-payments-advanced' ); 245 | /* translators: 1) Charge ID 2) Status. */ 246 | echo wp_kses_post( wpautop( sprintf( __( 'Charge %1$s is %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), esc_html( $charge_id ), esc_html( $charge_status_label ) ) ) ); 247 | 248 | $charge_status = $charge_cached_status->status; 249 | 250 | switch ( $charge_status ) { 251 | case 'AuthorizationInitiated': 252 | $actions['close_authorization'] = array( 253 | 'id' => $charge_id, 254 | 'button' => __( 'Close Authorization', 'woocommerce-gateway-amazon-payments-advanced' ), 255 | ); 256 | $need_refresh = true; 257 | break; 258 | case 'CaptureInitiated': 259 | $need_refresh = true; 260 | break; 261 | case 'Canceled': 262 | case 'Declined': 263 | break; 264 | case 'Authorized': 265 | $actions['capture'] = array( 266 | 'id' => $charge_id, 267 | 'button' => __( 'Capture funds', 'woocommerce-gateway-amazon-payments-advanced' ), 268 | ); 269 | 270 | $actions['close_authorization'] = array( 271 | 'id' => $charge_id, 272 | 'button' => __( 'Close Authorization', 'woocommerce-gateway-amazon-payments-advanced' ), 273 | ); 274 | $need_refresh = true; 275 | break; 276 | case 'Captured': 277 | // TODO: Handle fully refunded charges. 278 | $actions['refund'] = array( 279 | 'id' => $charge_id, 280 | 'button' => __( 'Make a refund?', 'woocommerce-gateway-amazon-payments-advanced' ), 281 | 'href' => '#toggle-refunds', 282 | ); 283 | break; 284 | default: 285 | // TODO: This is an unknown state, maybe handle? 286 | break; 287 | } 288 | } 289 | 290 | if ( ! $need_refresh ) { 291 | unset( $actions['refresh'] ); 292 | } 293 | 294 | $actions = apply_filters( 'woocommerce_amazon_pa_order_admin_actions', $actions, $order ); 295 | 296 | if ( ! empty( $actions ) ) { 297 | echo '

'; 298 | foreach ( $actions as $action_name => $action ) { 299 | $url = add_query_arg( 300 | array( 301 | 'amazon_action' => $action_name, 302 | 'amazon_id' => $action['id'], 303 | 'security' => wp_create_nonce( 'amazon_order_action' ), 304 | ) 305 | ); 306 | $actions[ $action_name ]['url'] = $url; 307 | if ( isset( $action['href'] ) ) { 308 | $url = $action['href']; 309 | } 310 | 311 | echo '' . esc_html( $action['button'] ) . ' '; 312 | } 313 | echo '

'; 314 | } 315 | } 316 | 317 | } 318 | -------------------------------------------------------------------------------- /includes/admin/legacy/class-wc-amazon-payments-advanced-order-admin-legacy.php: -------------------------------------------------------------------------------- 1 | get_id(); 39 | wc_apa()->log( sprintf( 'Info: Trying to perform "%s" for order #%s', $action, $order_id ) ); 40 | switch ( $action ) { 41 | case 'refresh': 42 | $this->clear_stored_states( $order_id ); 43 | break; 44 | case 'authorize': 45 | $order->delete_meta_data( 'amazon_authorization_id' ); 46 | $order->delete_meta_data( 'amazon_capture_id' ); 47 | $order->save(); 48 | 49 | // $id is order reference. 50 | wc_apa()->log( 'Info: Trying to authorize payment in order reference ' . $id ); 51 | 52 | WC_Amazon_Payments_Advanced_API_Legacy::authorize_payment( $order_id, $id, false ); 53 | $this->clear_stored_states( $order_id ); 54 | break; 55 | case 'authorize_capture': 56 | $order->delete_meta_data( 'amazon_authorization_id' ); 57 | $order->delete_meta_data( 'amazon_capture_id' ); 58 | $order->save(); 59 | 60 | // $id is order reference. 61 | wc_apa()->log( 'Info: Trying to authorize and capture payment in order reference ' . $id ); 62 | 63 | WC_Amazon_Payments_Advanced_API_Legacy::authorize_payment( $order_id, $id, true ); 64 | WC_Amazon_Payments_Advanced_API_Legacy::close_order_reference( $order_id ); 65 | $this->clear_stored_states( $order_id ); 66 | break; 67 | case 'close_authorization': 68 | // $id is authorization reference. 69 | wc_apa()->log( 'Info: Trying to close authorization ' . $id ); 70 | 71 | WC_Amazon_Payments_Advanced_API_Legacy::close_authorization( $order_id, $id ); 72 | $this->clear_stored_states( $order_id ); 73 | break; 74 | case 'capture': 75 | // $id is authorization reference. 76 | wc_apa()->log( 'Info: Trying to capture payment with authorization ' . $id ); 77 | 78 | WC_Amazon_Payments_Advanced_API_Legacy::capture_payment( $order_id, $id ); 79 | WC_Amazon_Payments_Advanced_API_Legacy::close_order_reference( $order_id ); 80 | $this->clear_stored_states( $order_id ); 81 | break; 82 | case 'refund': 83 | // $id is capture reference. 84 | wc_apa()->log( 'Info: Trying to refund payment with capture reference ' . $id ); 85 | // phpcs:disable WordPress.Security.NonceVerification.Missing 86 | $amazon_refund_amount = floatval( wc_clean( $_POST['amazon_refund_amount'] ) ); 87 | $amazon_refund_note = wc_clean( $_POST['amazon_refund_note'] ); 88 | // phpcs:enable WordPress.Security.nonceVerification.Missing 89 | WC_Amazon_Payments_Advanced_API_Legacy::refund_payment( $order_id, $id, $amazon_refund_amount, $amazon_refund_note ); 90 | wc_create_refund( 91 | array( 92 | 'amount' => $amazon_refund_amount, 93 | 'reason' => $amazon_refund_note, 94 | 'order_id' => $order_id, 95 | ) 96 | ); 97 | $this->clear_stored_states( $order_id ); 98 | break; 99 | default: 100 | do_action( 'woocommerce_amazon_pa_v1_order_admin_action_' . $action, $order, $id, $action ); 101 | break; 102 | } 103 | } 104 | 105 | /** 106 | * Wipe states so the value is refreshed. 107 | * 108 | * Invoked when refresh link is clicked. 109 | * 110 | * @param int $order_id Order ID. 111 | */ 112 | private function clear_stored_states( $order_id ) { 113 | $order = wc_get_order( $order_id ); 114 | if ( ! ( $order instanceof WC_Order ) ) { 115 | return; 116 | } 117 | 118 | $order->delete_meta_data( 'amazon_reference_state' ); 119 | $order->delete_meta_data( 'amazon_capture_state' ); 120 | $order->delete_meta_data( 'amazon_authorization_state' ); 121 | $order->save(); 122 | 123 | do_action( 'woocommerce_amazon_pa_v1_cleared_stored_states', $order_id ); 124 | } 125 | 126 | /** 127 | * Get the refresh link. 128 | * 129 | * Refresh link in Amazon Pay meta box is used to clear Amazon order state. 130 | * 131 | * @since 1.6.0 132 | * 133 | * @return string HTML of refresh link with its container 134 | */ 135 | private function get_refresh_link() { 136 | return wpautop( 137 | sprintf( 138 | '%s%s', 139 | esc_html__( 'Refresh', 'woocommerce-gateway-amazon-payments-advanced' ), 140 | wc_help_tip( __( 'Refresh Amazon transaction status.', 'woocommerce-gateway-amazon-payments-advanced' ) ) 141 | ) 142 | ); 143 | } 144 | 145 | /** 146 | * Authorization metabox content. 147 | * 148 | * @param WC_Order $order Order object. 149 | * @param string $version Version of the order. 150 | */ 151 | public function auth_box_render( $order, $version ) { 152 | if ( 'v1' !== strtolower( $version ) ) { 153 | return; 154 | } 155 | 156 | $actions = array( 157 | 'refresh' => true, 158 | ); 159 | $order_id = $order->get_id(); 160 | 161 | // Get ids. 162 | $amazon_authorization_id = $order->get_meta( 'amazon_authorization_id', true, 'edit' ); 163 | $amazon_reference_id = $order->get_meta( 'amazon_reference_id', true, 'edit' ); 164 | $amazon_capture_id = $order->get_meta( 'amazon_capture_id', true, 'edit' ); 165 | $amazon_refund_ids = wp_list_pluck( $order->get_meta( 'amazon_refund_id', false, 'edit' ), 'value' ); 166 | 167 | $override = apply_filters( 'woocommerce_amazon_pa_v1_order_admin_actions_panel', false, $order, $actions ); 168 | 169 | if ( is_array( $override ) ) { 170 | $actions = $override['actions']; 171 | } elseif ( $amazon_capture_id ) { 172 | 173 | $amazon_capture_state = WC_Amazon_Payments_Advanced_API_Legacy::get_capture_state( $order_id, $amazon_capture_id ); 174 | 175 | switch ( $amazon_capture_state ) { 176 | case 'Pending': 177 | /* translators: 1) Capture ID 2) Capture Status. */ 178 | echo wp_kses_post( sprintf( __( 'Capture Reference %1$s is %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), $amazon_capture_id, $amazon_capture_state ) ); 179 | 180 | // Admin will need to re-check this, so clear the stored value. 181 | $this->clear_stored_states( $order_id ); 182 | break; 183 | case 'Declined': 184 | echo esc_html__( 'The capture was declined.', 'woocommerce-gateway-amazon-payments-advanced' ); 185 | 186 | $actions['authorize'] = array( 187 | 'id' => $amazon_reference_id, 188 | 'button' => __( 'Re-authorize?', 'woocommerce-gateway-amazon-payments-advanced' ), 189 | ); 190 | 191 | break; 192 | case 'Completed': 193 | /* translators: 1) Capture ID 2) Capture Status. */ 194 | echo wp_kses_post( sprintf( __( 'Capture Reference %1$s is %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), $amazon_capture_id, $amazon_capture_state ) . ' ' . __( 'Make a refund?', 'woocommerce-gateway-amazon-payments-advanced' ) . '' ); 195 | 196 | // Refund form. 197 | ?> 198 |

'; 331 | 332 | foreach ( $actions as $action_name => $action ) { 333 | echo '' . esc_html( $action['button'] ) . ' '; 334 | } 335 | 336 | echo '

'; 337 | 338 | } 339 | 340 | $js = " 341 | jQuery( '#woocommerce-amazon-payments-advanced' ).on( 'click', 'a.button, a.refresh', function() { 342 | 343 | jQuery( '#woocommerce-amazon-payments-advanced' ).block({ 344 | message: null, 345 | overlayCSS: { 346 | background: '#fff url(" . WC()->plugin_url() . "/assets/images/ajax-loader.gif) no-repeat center', 347 | opacity: 0.6 348 | } 349 | }); 350 | 351 | var data = { 352 | action: 'amazon_order_action', 353 | security: '" . wp_create_nonce( 'amazon_order_action' ) . "', 354 | order_id: '$order_id', 355 | amazon_action: jQuery( this ).data( 'action' ), 356 | amazon_id: jQuery( this ).data( 'id' ), 357 | amazon_refund_amount: jQuery( '.amazon_refund_amount' ).val(), 358 | amazon_refund_note: jQuery( '.amazon_refund_note' ).val(), 359 | }; 360 | 361 | // Ajax action 362 | jQuery.ajax({ 363 | url: '" . admin_url( 'admin-ajax.php' ) . "', 364 | data: data, 365 | type: 'POST', 366 | success: function( result ) { 367 | location.reload(); 368 | } 369 | }); 370 | 371 | return false; 372 | }); 373 | 374 | jQuery( '#woocommerce-amazon-payments-advanced' ).on( 'click', 'a.toggle_refund', function() { 375 | jQuery( '.refund_form' ).slideToggle(); 376 | return false; 377 | }); 378 | "; 379 | 380 | wc_enqueue_js( $js ); 381 | } 382 | 383 | } 384 | -------------------------------------------------------------------------------- /includes/blocks/class-wc-amazon-payments-advanced-register-blocks.php: -------------------------------------------------------------------------------- 1 | array( 29 | 'frontend_script' => false, 30 | 'backend_style' => false, 31 | 'frontend_style' => false, 32 | ), 33 | 'log-out-banner' => array( 34 | 'frontend_script' => false, 35 | 'backend_style' => false, 36 | 'frontend_style' => true, 37 | ), 38 | ); 39 | 40 | /** 41 | * Register our hooks. 42 | */ 43 | public function __construct() { 44 | add_action( 'init', array( $this, 'register_amazon_blocks' ), 90 ); 45 | } 46 | 47 | /** 48 | * Registers Amazon Pay Blocks. 49 | * 50 | * @return void 51 | */ 52 | public function register_amazon_blocks() { 53 | /** 54 | * This blocks are only being used along with WooCommerce Blocks. 55 | * So if WooCommerce blocks isn't present, we bail registration. 56 | */ 57 | if ( ! class_exists( 'Automattic\WooCommerce\Blocks\Package' ) ) { 58 | return; 59 | } 60 | 61 | $plugin_root = wc_apa()->path; 62 | 63 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 64 | 65 | foreach ( self::BLOCKS as $block => $args ) { 66 | $blocks_json = $plugin_root . '/blocks-metadata/' . $block . '/block.json'; 67 | $script_data_file = $plugin_root . '/build/js/blocks/' . $block . '/index.asset.php'; 68 | 69 | if ( ! file_exists( $blocks_json ) || ! file_exists( $script_data_file ) ) { 70 | continue; 71 | } 72 | 73 | $script_helper_data = include $script_data_file; 74 | wp_register_script( 'amazon-payments-advanced-blocks-' . $block . '-editor', wc_apa()->plugin_url . '/build/js/blocks/' . $block . '/index' . $min . '.js', $script_helper_data['dependencies'], $script_helper_data['version'], true ); 75 | wp_set_script_translations( 'amazon-payments-advanced-blocks-' . $block . '-editor', 'woocommerce-gateway-amazon-payments-advanced' ); 76 | 77 | if ( ! empty( $args['frontend_script'] ) ) { 78 | $script_helper_data = include $plugin_root . '/build/js/blocks/' . $block . '/frontend.asset.php'; 79 | wp_register_script( 'amazon-payments-advanced-blocks-' . $block, wc_apa()->plugin_url . '/build/js/blocks/' . $block . '/frontend' . $min . '.js', $script_helper_data['dependencies'], $script_helper_data['version'], true ); 80 | wp_set_script_translations( 'amazon-payments-advanced-blocks-' . $block, 'woocommerce-gateway-amazon-payments-advanced' ); 81 | } 82 | 83 | if ( ! empty( $args['frontend_style'] ) ) { 84 | wp_register_style( 'amazon-payments-advanced-blocks-' . $block, wc_apa()->plugin_url . '/build/js/blocks/' . $block . '/style-index.css', array(), $script_helper_data['version'] ); 85 | } 86 | 87 | if ( ! empty( $args['backend_style'] ) ) { 88 | wp_register_style( 'amazon-payments-advanced-blocks-' . $block . '-editor', wc_apa()->plugin_url . '/build/js/blocks/' . $block . '/index.css', array(), $script_helper_data['version'] ); 89 | } 90 | 91 | foreach ( self::CUSTOM_ARGS as $custom_arg ) { 92 | if ( ! isset( $args[ $custom_arg ] ) ) { 93 | continue; 94 | } 95 | 96 | unset( $args[ $custom_arg ] ); 97 | } 98 | 99 | register_block_type_from_metadata( $blocks_json, $args ); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-alexa-notifications.php: -------------------------------------------------------------------------------- 1 | Status -> Logs while testing your 34 | * integration. 35 | * 36 | * $carrier should be supported by Amazon API. 37 | * 38 | * @see https://developer.amazon.com/docs/amazon-pay-checkout/setting-up-delivery-notifications.html 39 | * 40 | * For a list of supported carriers, 41 | * @see https://eps-eu-external-file-share.s3.eu-central-1.amazonaws.com/Alexa/Delivery+Notifications/amazon-pay-delivery-tracker-supported-carriers-v2.csv 42 | * 43 | * @throws Exception On error, but the functions catches it and logs it. 44 | * 45 | * @param mixed $tracking_number The tracking number provided by the carrier. 46 | * @param string $carrier The carrier code through the shipping is being handled. 47 | * @param string|int $order_id The order id which the tracking number refers to. 48 | * @return void 49 | */ 50 | public function enable_alexa_notifications_for_carrier( $tracking_number, $carrier, $order_id ) { 51 | if ( empty( $tracking_number ) || empty( $order_id ) || empty( $carrier ) ) { 52 | return; 53 | } 54 | $order = wc_get_order( $order_id ); 55 | 56 | /* If we cant retrieve the order or if the order doesn't needs shipping we bail. */ 57 | if ( ! class_exists( 'WC_Order' ) || ! ( $order instanceof \WC_Order ) || count( $order->get_items( 'shipping' ) ) <= 0 ) { 58 | return; 59 | } 60 | 61 | /* Allow third party plugins to provide a charge permission id. */ 62 | $charge_permission_id = apply_filters( 'woocommerce_amazon_pa_alexa_notification_charge_permission_id', $order->get_meta( 'amazon_charge_permission_id' ), $order, $carrier ); 63 | 64 | /* If the order wan't completed through Amazon Pay or if there is no charge permission id we bail. */ 65 | if ( 'amazon_payments_advanced' !== $order->get_payment_method() || ! $charge_permission_id ) { 66 | return; 67 | } 68 | 69 | /* Allow third party plugins to alter the payload used for activating Alexa Delivery Notifications. */ 70 | $payload = apply_filters( 71 | 'apa_alexa_notification_payload', 72 | array( 73 | 'chargePermissionId' => $charge_permission_id, 74 | 'deliveryDetails' => array( 75 | array( 76 | 'trackingNumber' => $tracking_number, 77 | 'carrierCode' => $carrier, 78 | ), 79 | ), 80 | ), 81 | $order, 82 | $carrier 83 | ); 84 | 85 | try { 86 | 87 | /* Bail early if class WC_Amazon_Payments_Advanced_API is not available for some reason. */ 88 | if ( ! class_exists( 'WC_Amazon_Payments_Advanced_API' ) ) { 89 | throw new Exception( 'Class WC_Amazon_Payments_Advanced_API does not exists!', 1001 ); 90 | } 91 | 92 | $result = WC_Amazon_Payments_Advanced_API::trigger_alexa_notifications( $payload ); 93 | 94 | if ( ! empty( $result['status'] ) && 200 === $result['status'] ) { 95 | /* Log the successful result. */ 96 | wc_apa()->log( 'Successfully enabled Alexa Delivery Notifications for order #' . $order_id . ' with charge permission id ' . $charge_permission_id . ' and got the below response', $result['response'] ); 97 | } else { 98 | /* Log the error provided by Amazon. */ 99 | wc_apa()->log( 'Failed to enable Alexa Delivery Notifications for order #' . $order_id . ' with charge permission id ' . $charge_permission_id . ' with status: ' . $result['status'] . ' and got the below response', $result['response'] ); 100 | } 101 | do_action( 'woocommerce_amazon_pa_alexa_notification_result', $result, $order, $payload ); 102 | } catch ( Exception $e ) { 103 | /* Log any Exceptions. */ 104 | wc_apa()->log( 'Exception occurred while trying to enable Alexa Delivery Notifications for order #' . $order_id . ' with charge permission id ' . $charge_permission_id . ' with code: ' . $e->getCode() . ' and message: ' . $e->getMessage() ); 105 | do_action( 'woocommerce_amazon_pa_alexa_notification_exception', $e, $order, $payload ); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-api-abstract.php: -------------------------------------------------------------------------------- 1 | 'https://payments.amazon.com/documentation/express/201728550', 27 | 'gb' => 'https://amazonpayments.s3.amazonaws.com/documents/Get_Your_Login_with_Amazon_Client_ID_EU_ENG.pdf?ld=APUSLPADefault', 28 | 'eu' => 'https://amazonpayments.s3.amazonaws.com/documents/Get_Your_Login_with_Amazon_Client_ID_EU_ENG.pdf?ld=APUSLPADefault', 29 | ); 30 | 31 | /** 32 | * Language ISO code map to its domain. 33 | * 34 | * @var array 35 | */ 36 | public static $lang_domains_mapping = array( 37 | 'en-GB' => 'co.uk', 38 | 'de-DE' => 'de', 39 | 'fr-FR' => 'fr', 40 | 'it-IT' => 'it', 41 | 'es-ES' => 'es', 42 | 'en-US' => 'com', 43 | 'ja-JP' => 'co.jp', 44 | ); 45 | 46 | /** 47 | * List of supported currencies. 48 | * https://pay.amazon.com/uk/help/5BDCWHCUC27485L 49 | * 50 | * @var array 51 | */ 52 | protected static $supported_currencies = array( 53 | 'AUD', 54 | 'GBP', 55 | 'DKK', 56 | 'EUR', 57 | 'HKD', 58 | 'JPY', 59 | 'NZD', 60 | 'NOK', 61 | 'ZAR', 62 | 'SEK', 63 | 'CHF', 64 | 'USD', 65 | ); 66 | 67 | /** 68 | * Simple Path registration urls. 69 | * 70 | * @var array 71 | */ 72 | public static $registration_urls = array( 73 | 'us' => 'https://payments.amazon.com/register', 74 | 'gb' => 'https://payments-eu.amazon.com/register', 75 | 'eu' => 'https://payments-eu.amazon.com/register', 76 | 'jp' => 'https://pay.amazon.com/jp/signup', // Simple Path not available in jp yet, just a normal url. 77 | ); 78 | 79 | /** 80 | * Simple Path public keys urls. 81 | * 82 | * @var array 83 | */ 84 | public static $get_public_keys_urls = array( 85 | 'us' => 'https://payments.amazon.com/register/getpublickey', 86 | 'gb' => 'https://payments-eu.amazon.com/register/getpublickey', 87 | 'eu' => 'https://payments-eu.amazon.com/register/getpublickey', 88 | 'jp' => '', // Not available in jp yet. 89 | ); 90 | 91 | /** 92 | * Simple Path spIds. 93 | * 94 | * @var array 95 | */ 96 | public static $sp_ids = array( 97 | 'us' => 'A1BVJDFFHQ7US4', 98 | 'gb' => 'A3AO8502KEOZS3', 99 | 'eu' => 'A3V6YX13IG1QFQ', 100 | 'jp' => 'A2EBW2CGZKMGE4', 101 | ); 102 | 103 | /** 104 | * Simple Onboarding Version. 105 | * 106 | * @var int 107 | */ 108 | public static $onboarding_version = 2; 109 | 110 | /** 111 | * Get settings 112 | * 113 | * @param string $key Key, if retrieving a single key. 114 | * 115 | * @return array|mixed 116 | */ 117 | public static function get_settings( $key = null ) { 118 | $settings_options_name = 'woocommerce_amazon_payments_advanced_settings'; 119 | 120 | $settings = (array) get_option( $settings_options_name, array() ); 121 | $default = array( 122 | 'enabled' => 'yes', 123 | 'title' => __( 'Amazon Pay', 'woocommerce-gateway-amazon-payments-advanced' ), 124 | 'description' => __( 'Complete your payment using Amazon Pay!', 'woocommerce-gateway-amazon-payments-advanced' ), 125 | 'merchant_id' => '', 126 | 'store_id' => '', 127 | 'public_key_id' => '', 128 | 'seller_id' => '', 129 | 'mws_access_key' => '', 130 | 'secret_key' => '', 131 | 'payment_region' => self::get_payment_region_from_country( WC()->countries->get_base_country() ), 132 | 'enable_login_app' => ( self::is_new_installation() ) ? 'yes' : 'no', 133 | 'app_client_id' => '', 134 | 'sandbox' => 'yes', 135 | 'payment_capture' => 'no', 136 | 'authorization_mode' => 'async', 137 | 'redirect_authentication' => 'popup', 138 | 'cart_button_display_mode' => 'button', 139 | 'button_type' => 'LwA', 140 | 'button_size' => 'small', 141 | 'button_color' => 'Gold', 142 | 'button_language' => '', 143 | 'debug' => 'no', 144 | 'hide_button_mode' => 'no', 145 | 'amazon_keys_setup_and_validated' => '0', 146 | 'subscriptions_enabled' => 'yes', 147 | 'mini_cart_button' => 'no', 148 | 'product_button' => 'no', 149 | 'alexa_notifications_support' => 'no', 150 | ); 151 | 152 | $settings = apply_filters( 'woocommerce_amazon_pa_settings', array_merge( $default, $settings ) ); 153 | 154 | if ( is_null( $key ) ) { 155 | return $settings; 156 | } else { 157 | return isset( $settings[ $key ] ) ? $settings[ $key ] : null; 158 | } 159 | } 160 | 161 | /** 162 | * Get payment region based on a given country. 163 | * 164 | * @since 1.6.3 165 | * 166 | * @param string $country Country code. 167 | * @param string $default Default country code. Default to 'us' or 'eu' if 168 | * passed country is in EU union. 169 | * 170 | * @return string Payment region 171 | */ 172 | public static function get_payment_region_from_country( $country, $default = 'us' ) { 173 | switch ( $country ) { 174 | case 'GB': 175 | case 'US': 176 | case 'JP': 177 | $region = strtolower( $country ); 178 | break; 179 | default: 180 | $region = $default; 181 | if ( in_array( $country, WC()->countries->get_european_union_countries(), true ) ) { 182 | $region = 'eu'; 183 | } 184 | } 185 | 186 | if ( ! array_key_exists( $region, self::get_payment_regions() ) ) { 187 | $region = 'us'; 188 | } 189 | 190 | return $region; 191 | } 192 | 193 | /** 194 | * Get payment regions. 195 | * 196 | * @since 1.6.3 197 | * 198 | * @return array Payment regions 199 | */ 200 | public static function get_payment_regions() { 201 | return array( 202 | 'eu' => __( 'Euro Region', 'woocommerce-gateway-amazon-payments-advanced' ), 203 | 'gb' => __( 'United Kingdom', 'woocommerce-gateway-amazon-payments-advanced' ), 204 | 'us' => __( 'United States', 'woocommerce-gateway-amazon-payments-advanced' ), 205 | 'jp' => __( 'Japan', 'woocommerce-gateway-amazon-payments-advanced' ), 206 | ); 207 | } 208 | 209 | /** 210 | * Checks whether current payment region supports shop currency. 211 | * 212 | * @since 1.8.0 213 | * @version 1.8.0 214 | * 215 | * @return bool Returns true if shop currency is supported by current payment region. 216 | */ 217 | public static function is_region_supports_shop_currency() { 218 | $region = self::get_region(); 219 | // Take into consideration external multi-currency plugins when not supported multicurrency region. 220 | $currency = apply_filters( 'woocommerce_amazon_pa_active_currency', get_option( 'woocommerce_currency' ) ); 221 | 222 | switch ( $region ) { 223 | case 'eu': 224 | return 'EUR' === $currency; 225 | case 'gb': 226 | return 'GBP' === $currency; 227 | case 'us': 228 | return 'USD' === $currency; 229 | case 'jp': 230 | return 'JPY' === $currency; 231 | } 232 | 233 | return false; 234 | } 235 | 236 | /** 237 | * Get location. 238 | * 239 | * @deprecated 240 | */ 241 | public static function get_location() { 242 | _deprecated_function( __METHOD__, '1.6.3', 'WC_Amazon_Payments_Advanced_API::get_region' ); 243 | return self::get_region(); 244 | } 245 | 246 | /** 247 | * Get payment region from setting. 248 | * 249 | * @return string 250 | */ 251 | public static function get_region() { 252 | $settings = self::get_settings(); 253 | $region = ! empty( $settings['payment_region'] ) ? $settings['payment_region'] : self::get_payment_region_from_country( WC()->countries->get_base_country() ); 254 | 255 | return $region; 256 | } 257 | 258 | /** 259 | * Get the label of payment region from setting. 260 | * 261 | * @since 1.8.0 262 | * @version 1.8.0 263 | * @param string $region Region, if checking for a specific region. If not defined, will get label for current region. 264 | * 265 | * @return string Payment region label. 266 | */ 267 | public static function get_region_label( $region = null ) { 268 | if ( is_null( $region ) ) { 269 | $region = self::get_region(); 270 | } 271 | $regions = self::get_payment_regions(); 272 | return isset( $regions[ $region ] ) ? $regions[ $region ] : ''; 273 | } 274 | 275 | /** 276 | * Get Login with Amazon App setup URL. 277 | * 278 | * @return string 279 | */ 280 | public static function get_client_id_instructions_url() { 281 | $region = self::get_region(); 282 | 283 | return array_key_exists( $region, self::$client_id_instructions ) ? self::$client_id_instructions[ $region ] : ''; 284 | } 285 | 286 | /** 287 | * Get if amazon keys have been set and validated. 288 | * 289 | * @return bool 290 | */ 291 | public static function get_amazon_keys_set() { 292 | $settings = self::get_settings(); 293 | return ( isset( $settings['amazon_keys_setup_and_validated'] ) ) && ( 1 === $settings['amazon_keys_setup_and_validated'] ); 294 | } 295 | 296 | /** 297 | * Get list of supported currencies. 298 | * 299 | * @param bool $filter Filters current woocommerce currency. 300 | * 301 | * @return array 302 | */ 303 | public static function get_supported_currencies( $filter = false ) { 304 | return array_combine( self::$supported_currencies, self::$supported_currencies ); 305 | } 306 | 307 | /** 308 | * Returns selected currencies on settings together with native woocommerce currency. 309 | * 310 | * @return array 311 | */ 312 | public static function get_selected_currencies() { 313 | $settings = self::get_settings(); 314 | return isset( $settings['currencies_supported'] ) && is_array( $settings['currencies_supported'] ) ? $settings['currencies_supported'] : array(); 315 | } 316 | 317 | /** 318 | * Returns the current value for the authorization mode setting. 319 | * 320 | * @return array 321 | */ 322 | public static function get_authorization_mode() { 323 | $settings = self::get_settings(); 324 | return $settings['authorization_mode']; 325 | } 326 | 327 | /** 328 | * Check if it is a new merchant (new install). 329 | * 330 | * @return bool 331 | */ 332 | public static function is_new_installation() { 333 | $version = get_option( WC_Amazon_Payments_Advanced_Install::APA_NEW_INSTALL_OPTION ); 334 | return (bool) $version; 335 | } 336 | 337 | /** 338 | * VAT registered sellers - Obtaining the Billing Address. 339 | * 340 | * @see http://docs.developer.amazonservices.com/en_UK/apa_guide/APAGuide_GetAuthorizationStatus.html 341 | * 342 | * @param int $order_id Order ID. 343 | * @param object $result Result from API response. 344 | * 345 | * @deprecated 346 | */ 347 | public static function maybe_update_billing_details( $order_id, $result ) { 348 | _deprecated_function( 'WC_Amazon_Payments_Advanced_API::maybe_update_billing_details', '1.6.0', 'WC_Amazon_Payments_Advanced_API_Legacy::update_order_billing_address' ); 349 | 350 | // @codingStandardsIgnoreStart 351 | if ( ! empty( $result->AuthorizationBillingAddress ) ) { 352 | $address = (array) $result->AuthorizationBillingAddress; 353 | 354 | WC_Amazon_Payments_Advanced_API_Legacy::update_order_billing_address( $order_id, $address ); 355 | } 356 | // @codingStandardsIgnoreEnd 357 | } 358 | 359 | /** 360 | * Remove address fields that have a string value of "undefined". 361 | * 362 | * @param SimpleXMLElement $address Address object from Amazon Pay API. 363 | */ 364 | private static function remove_undefined_strings( $address ) { 365 | if ( ! $address instanceof SimpleXMLElement ) { 366 | return; 367 | } 368 | $nodes_to_remove = array(); 369 | foreach ( $address->children() as $child ) { 370 | if ( 'undefined' === (string) $child ) { 371 | array_push( $nodes_to_remove, $child ); 372 | } 373 | } 374 | foreach ( $nodes_to_remove as $node ) { 375 | unset( $node[0] ); 376 | } 377 | } 378 | 379 | /** 380 | * Format an Amazon Pay Name for WooCommerce. 381 | * 382 | * @param string $name Amazon Pay full name field. 383 | */ 384 | public static function format_name( $name ) { 385 | // $name could be empty for non-Login app clients. Set both first and last name as '' in those cases. 386 | if ( empty( $name ) ) { 387 | return array( 388 | 'first_name' => '', 389 | 'last_name' => '', 390 | ); 391 | } 392 | // Use fallback value for the last name to avoid field required errors. 393 | $last_name_fallback = '–'; 394 | $names = preg_split( '/ | /', $name ); 395 | return array( 396 | 'first_name' => array_shift( $names ), 397 | 'last_name' => empty( $names ) ? $last_name_fallback : implode( ' ', $names ), 398 | ); 399 | } 400 | 401 | /** 402 | * Format an Amazon Pay Address DataType for WooCommerce. 403 | * 404 | * @see https://payments.amazon.com/documentation/apireference/201752430 405 | * 406 | * @param object $address Address object from Amazon Pay API. 407 | * 408 | * @return array Address formatted for WooCommerce. 409 | */ 410 | public static function format_address( $address ) { 411 | // Some address fields could have a string value of "undefined", causing issues when formatting it. 412 | self::remove_undefined_strings( $address ); 413 | 414 | // Get first and last names. 415 | // @codingStandardsIgnoreStart 416 | $address_name = ! empty( $address->Name ) ? (string) $address->Name : ''; 417 | // @codingStandardsIgnoreEnd 418 | $formatted = self::format_name( $address_name ); 419 | 420 | // Special handling for German speaking countries. 421 | // 422 | // @see https://github.com/woothemes/woocommerce-gateway-amazon-payments-advanced/issues/25 423 | // @codingStandardsIgnoreStart 424 | if ( ! empty( $address->CountryCode ) && in_array( $address->CountryCode, array( 'AT', 'DE' ) ) ) { 425 | 426 | $address_parts = array_filter( array( 427 | (string) $address->AddressLine1, 428 | (string) $address->AddressLine2, 429 | (string) $address->AddressLine3) 430 | ); 431 | $formatted['address_1'] = array_pop( $address_parts ); 432 | $formatted['company'] = implode( ' ', $address_parts ); 433 | 434 | } elseif ( ! empty( $address->CountryCode ) && in_array( $address->CountryCode, array( 'JP' ) ) ) { 435 | 436 | if ( ! empty( $address->AddressLine1 ) ) { 437 | $formatted['address_1'] = (string) $address->AddressLine1; 438 | } 439 | 440 | if ( ! empty( $address->AddressLine2 ) ) { 441 | $formatted['address_2'] = (string) $address->AddressLine2; 442 | } 443 | 444 | if ( ! empty( $address->AddressLine3 ) ) { 445 | $formatted['company'] = (string) $address->AddressLine3; 446 | } 447 | 448 | } else { 449 | 450 | // Format address and map to WC fields 451 | $address_lines = array(); 452 | 453 | if ( ! empty( $address->AddressLine1 ) ) { 454 | $address_lines[] = (string) $address->AddressLine1; 455 | } 456 | if ( ! empty( $address->AddressLine2 ) ) { 457 | $address_lines[] = (string) $address->AddressLine2; 458 | } 459 | if ( ! empty( $address->AddressLine3 ) ) { 460 | $address_lines[] = (string) $address->AddressLine3; 461 | } 462 | 463 | if ( 3 === count( $address_lines ) ) { 464 | 465 | $formatted['company'] = $address_lines[0]; 466 | $formatted['address_1'] = $address_lines[1]; 467 | $formatted['address_2'] = $address_lines[2]; 468 | 469 | } elseif ( 2 === count( $address_lines ) ) { 470 | 471 | $formatted['address_1'] = $address_lines[0]; 472 | $formatted['address_2'] = $address_lines[1]; 473 | 474 | } elseif ( count( $address_lines ) ) { 475 | $formatted['address_1'] = $address_lines[0]; 476 | } 477 | 478 | } 479 | 480 | // Prevent invalid characters in phone number. 481 | $formatted['phone'] = isset( $address->Phone ) ? (string) filter_var($address->Phone, FILTER_DEFAULT, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH) : null; 482 | 483 | $formatted['city'] = isset( $address->City ) ? (string) $address->City : null; 484 | if ( ! empty( $address->CountryCode ) && in_array( $address->CountryCode, array( 'JP' ) ) ) { 485 | if ( empty( $formatted['city'] ) ) { 486 | $formatted['city'] = '–'; // Force empty city 487 | } 488 | } 489 | $formatted['postcode'] = isset( $address->PostalCode ) ? (string) $address->PostalCode : null; 490 | $formatted['state'] = isset( $address->StateOrRegion ) ? (string) $address->StateOrRegion : null; 491 | $formatted['country'] = isset( $address->CountryCode ) ? (string) $address->CountryCode : null; 492 | 493 | // Handle missmatches of states in AMZ and WC 494 | if ( ! is_null( $formatted['state'] ) ) { 495 | $valid_states = WC()->countries->get_states( $formatted['country'] ); 496 | 497 | if ( ! empty( $valid_states ) && is_array( $valid_states ) ) { 498 | $valid_state_values = array_map( 'wc_strtoupper', array_flip( array_map( 'wc_strtoupper', $valid_states ) ) ); 499 | $uc_state = wc_strtoupper( $formatted['state'] ); 500 | 501 | $uc_state = WC_Gateway_Amazon_Payments_Advanced::maybe_get_jp_region_code( $uc_state ); 502 | 503 | if ( isset( $valid_state_values[ $uc_state ] ) ) { 504 | // With this part we consider state value to be valid as well, convert it to the state key for the valid_states check below. 505 | $uc_state = $valid_state_values[ $uc_state ]; 506 | } 507 | 508 | if ( ! in_array( $uc_state, $valid_state_values, true ) ) { 509 | $formatted['state'] = null; 510 | } else { 511 | $formatted['state'] = $uc_state; 512 | } 513 | } 514 | } 515 | // @codingStandardsIgnoreEnd 516 | 517 | $formatted = array_filter( 518 | $formatted, 519 | function( $v ) { 520 | return ! is_null( $v ); 521 | } 522 | ); 523 | 524 | return $formatted; 525 | 526 | } 527 | 528 | /** 529 | * Format the charge amount, make sure that decimals with JPY is 0. 530 | * 531 | * @param array $charge_amount The charge amount. 532 | */ 533 | public static function format_charge_amount( $charge_amount ) { 534 | 535 | if ( empty( $charge_amount['currencyCode'] ) || empty( $charge_amount['amount'] ) ) { 536 | return $charge_amount; 537 | } 538 | 539 | switch ( $charge_amount['currencyCode'] ) { 540 | case 'JPY': 541 | $charge_amount['amount'] = WC_Amazon_Payments_Advanced::format_amount( $charge_amount['amount'], 0 ); 542 | } 543 | 544 | return $charge_amount; 545 | } 546 | 547 | /** 548 | * Validate API Keys signature 549 | * 550 | * @return bool 551 | */ 552 | public static function validate_api_keys() { 553 | return false; 554 | } 555 | 556 | /** 557 | * Returns extra headers to be added to requests against Amazon Pay API. 558 | * 559 | * @return array 560 | */ 561 | protected static function get_amazon_pay_platform_headers() { 562 | $version_suffix = wc_apa()->get_gateway() instanceof WC_Gateway_Amazon_Payments_Advanced_Legacy ? '-legacy' : ''; 563 | 564 | return array( 565 | 'x-amz-pay-platform-version' => WC()->version, 566 | 'x-amz-pay-integrator-version' => wc_apa()->version . $version_suffix, 567 | 'x-amz-pay-integrator-id' => static::AMAZON_PAY_FOR_WOOCOMMERCE_SP_ID, 568 | ); 569 | } 570 | } 571 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-compat.php: -------------------------------------------------------------------------------- 1 | require_compats(); // Need to require early for some static methods to be available. 20 | add_action( 'woocommerce_amazon_pa_init', array( $this, 'load_compats' ) ); 21 | add_action( 'woocommerce_amazon_pa_init', array( $this, 'load_multicurrency' ) ); 22 | add_action( 'woocommerce_blocks_loaded', array( $this, 'load_block_compatibility' ) ); 23 | } 24 | 25 | /** 26 | * Load compat classes and instantiate it. 27 | */ 28 | public function require_compats() { 29 | // Require built-in compat classes. 30 | require_once 'compats/class-wc-amazon-payments-advanced-drip-compat.php'; 31 | require_once 'compats/class-wc-amazon-payments-advanced-wgm-compat.php'; 32 | require_once 'compats/class-wc-amazon-payments-advanced-dynamic-pricing-compat.php'; 33 | require_once 'compats/class-wc-amazon-payments-advanced-subscribe-to-newsletter-compat.php'; 34 | require_once 'compats/class-wc-amazon-payments-advanced-woocommerce-multilingual-compat.php'; 35 | 36 | // Require multi-currency compat class. 37 | require_once 'compats/class-wc-amazon-payments-advanced-multi-currency.php'; 38 | } 39 | 40 | /** 41 | * Load compat classes and instantiate it. 42 | */ 43 | public function load_compats() { 44 | $compats = array( 45 | 'WC_Amazon_Payments_Advanced_Drip_Compat', 46 | 'WC_Amazon_Payments_Advanced_WGM_Compat', 47 | 'WC_Amazon_Payments_Advanced_Dynamic_Pricing_Compat', 48 | 'WC_Amazon_Payments_Advanced_Subscribe_To_Newsletter_Compat', 49 | 'WC_Amazon_Payments_Advanced_Woocommerce_Multilingual_Compat', 50 | ); 51 | 52 | /** 53 | * Filters the WooCommerce Amazon Pay compats. 54 | * 55 | * @since 1.6.0 56 | * 57 | * @param array $compats List of class names that provide compatibilities 58 | * with WooCommerce Amazon Pay. 59 | */ 60 | $compats = apply_filters( 'woocommerce_amazon_pa_compats', $compats ); 61 | foreach ( $compats as $compat ) { 62 | if ( class_exists( $compat ) ) { 63 | new $compat(); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * Init Multicurrency hooks 70 | */ 71 | public function load_multicurrency() { 72 | WC_Amazon_Payments_Advanced_Multi_Currency::init(); 73 | } 74 | 75 | /** 76 | * Loads the WooCommerce Block Compatibility Classes, 77 | * when the WooCommerce Blocks Plugin is active. 78 | * 79 | * @return void 80 | */ 81 | public function load_block_compatibility() { 82 | if ( WC_Amazon_Payments_Advanced_Merchant_Onboarding_Handler::get_migration_status() && class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) && file_exists( __DIR__ . '/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compatibility.php' ) ) { 83 | require_once __DIR__ . '/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compatibility.php'; 84 | add_action( 'woocommerce_blocks_payment_method_type_registration', array( WC_Amazon_Payments_Advanced_Block_Compatibility::class, 'init' ) ); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-install.php: -------------------------------------------------------------------------------- 1 | version ); 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-ipn-handler-abstract.php: -------------------------------------------------------------------------------- 1 | log( 'Received Onboarding Key Exchage request.' ); 45 | 46 | $headers = $this->get_all_headers(); 47 | $registration_country = $this->get_country_origin_from_header( $headers ); 48 | $raw_post_data = $this->get_raw_post_data(); 49 | parse_str( $raw_post_data, $body ); 50 | 51 | try { 52 | $payload = array(); 53 | if ( isset( $body['payload'] ) ) { 54 | $payload = json_decode( $body['payload'], true ); 55 | } 56 | // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 57 | $payload = (object) filter_var_array( 58 | $payload, 59 | array( 60 | 'merchantId' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, 61 | 'storeId' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, 62 | 'publicKeyId' => FILTER_SANITIZE_FULL_SPECIAL_CHARS, 63 | ), 64 | true 65 | ); 66 | // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 67 | if ( ! isset( $payload->merchantId, $payload->storeId, $payload->publicKeyId ) ) { 68 | throw new Exception( esc_html__( 'Unable to import Amazon keys. Please verify your JSON format and values.', 'woocommerce-gateway-amazon-payments-advanced' ) ); 69 | } 70 | // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 71 | $public_key_id = rawurldecode( $payload->publicKeyId ); 72 | $decrypted_key = $this->decrypt_encrypted_public_key_id( $public_key_id ); 73 | // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 74 | $payload->publicKeyId = $decrypted_key; 75 | 76 | $this->save_payload( $payload ); 77 | header( 'Access-Control-Allow-Origin: ' . $this->get_origin_header( $headers ) ); 78 | header( 'Access-Control-Allow-Methods: GET, POST' ); 79 | header( 'Access-Control-Allow-Headers: Content-Type' ); 80 | wp_send_json( array( 'result' => 'success' ), 200 ); 81 | } catch ( Exception $e ) { 82 | wc_apa()->log( 'Failed to handle automatic key exchange request: ' . $e->getMessage() ); 83 | wp_send_json( 84 | array( 85 | 'result' => 'error', 86 | 'message' => esc_html__( 'Bad request.', 'woocommerce-gateway-amazon-payments-advanced' ) . ' ' . $e->getMessage(), 87 | ), 88 | 400 89 | ); 90 | } 91 | } 92 | 93 | /** 94 | * Decrypt the encryptedKey value from the encrypted credential payload. 95 | * This gives you the key that was used to encrypt the encryptedPayload value. 96 | * a. Base64decode the encryptedKey value from the encrypted credential payload. 97 | * b. Use the private decrypt function of the openSSL package (specifying the OPENSSL_PKCS1_OAEP_PADDING algorithm), 98 | * to decrypt the result of 2a, passing in the private key that was generated on opening the workflow. 99 | * 100 | * @param string $public_key_id Public Key ID. 101 | * 102 | * @return string|bool 103 | * @throws Exception On Errors. 104 | */ 105 | protected function decrypt_encrypted_public_key_id( $public_key_id ) { 106 | $decrypted_key = null; 107 | 108 | $private_keys = $this->get_temp_private_keys(); 109 | $private_keys = array_reverse( $private_keys ); // it's more likely that the last one is the one that works. 110 | 111 | $found = false; 112 | 113 | foreach ( $private_keys as $private_key ) { 114 | $res = openssl_private_decrypt( 115 | base64_decode( $public_key_id ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode, WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 116 | $decrypted_key, 117 | $private_key 118 | ); 119 | 120 | if ( $res ) { 121 | $found = $private_key; 122 | break; 123 | } 124 | } 125 | 126 | if ( ! $found ) { 127 | return false; 128 | } 129 | 130 | update_option( self::KEYS_OPTION_PRIVATE_KEY, $found ); 131 | delete_option( self::KEYS_OPTION_TEMP_PRIVATE_KEYS ); 132 | 133 | return $decrypted_key; 134 | } 135 | 136 | /** 137 | * Get Simple path registration URL. 138 | * 139 | * @return string Notify URL. 140 | */ 141 | public function get_simple_path_registration_url() { 142 | return WC()->api_request_url( self::ENDPOINT_URL ); 143 | } 144 | 145 | /** 146 | * Retrieves the raw request data (body). 147 | * 148 | * `$HTTP_RAW_POST_DATA` is deprecated in PHP 5.6 and removed in PHP 5.7, 149 | * it's used here for server that has issue with reading `php://input` 150 | * stream. 151 | * 152 | * @since 1.8.0 153 | * @version 1.8.0 154 | * 155 | * @return string Raw request data. 156 | */ 157 | protected function get_raw_post_data() { 158 | // phpcs:disable PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved 159 | global $HTTP_RAW_POST_DATA; 160 | 161 | if ( ! isset( $HTTP_RAW_POST_DATA ) ) { 162 | $HTTP_RAW_POST_DATA = file_get_contents( 'php://input' ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited 163 | } 164 | 165 | return $HTTP_RAW_POST_DATA; 166 | // phpcs:enable PHPCompatibility.Variables.RemovedPredefinedGlobalVariables.http_raw_post_dataDeprecatedRemoved 167 | } 168 | 169 | /** 170 | * Return temporary private keys 171 | * 172 | * @return array 173 | */ 174 | protected function get_temp_private_keys() { 175 | $temps = get_option( self::KEYS_OPTION_TEMP_PRIVATE_KEYS, array() ); 176 | if ( ! is_array( $temps ) ) { 177 | $temps = array(); 178 | } 179 | return $temps; 180 | } 181 | 182 | /** 183 | * The public key generated by the Ecommerce provider or plugin, which is used to encrypt the contents of the key 184 | * exchange package when supported by the registration workflow. This is an ephemeral 2048 bit RSA public key. 185 | * 186 | * @param bool $public Returns public or private key. 187 | * @return mixed 188 | * @throws Exception On Errors. 189 | */ 190 | protected function generate_keys( $public = false ) { 191 | 192 | if ( ! function_exists( 'openssl_pkey_new' ) || ! function_exists( 'openssl_verify' ) ) { 193 | throw new Exception( esc_html__( 'OpenSSL extension is not available in your server.', 'woocommerce-gateway-amazon-payments-advanced' ) ); 194 | } 195 | $keys = openssl_pkey_new( 196 | array( 197 | 'digest_alg' => self::DIGEST_ALG, 198 | 'private_key_bits' => self::PRIVATE_KEY_BITS, 199 | 'private_key_type' => self::PRIVATE_KEY_TYPE, 200 | ) 201 | ); 202 | 203 | $public_key = openssl_pkey_get_details( $keys ); 204 | openssl_pkey_export( $keys, $private_key ); 205 | 206 | $temps = $this->get_temp_private_keys(); 207 | 208 | $temps[] = $private_key; 209 | update_option( self::KEYS_OPTION_TEMP_PRIVATE_KEYS, $temps ); 210 | 211 | return ( $public ) ? $public_key['key'] : $private_key; 212 | } 213 | 214 | /** 215 | * Destroy public/private key generated for keys exchange. 216 | */ 217 | public static function destroy_keys() { 218 | delete_option( self::KEYS_OPTION_PRIVATE_KEY ); 219 | } 220 | 221 | /** 222 | * Gets amazon gateway settings and update them with the new credentials from exchange. 223 | * 224 | * @param object $payload Payload received from Amazon. 225 | */ 226 | protected function save_payload( $payload ) { 227 | $settings = WC_Amazon_Payments_Advanced_API::get_settings(); 228 | 229 | $settings['merchant_id'] = $payload->merchantId; // phpcs:ignore WordPress.NamingConventions 230 | $settings['store_id'] = $payload->storeId; // phpcs:ignore WordPress.NamingConventions 231 | $settings['public_key_id'] = $payload->publicKeyId; // phpcs:ignore WordPress.NamingConventions 232 | $settings['amazon_keys_setup_and_validated'] = 1; 233 | update_option( 'woocommerce_amazon_payments_advanced_settings', $settings ); 234 | update_option( 'woocommerce_amazon_payments_advanced_saved_payload', true ); 235 | } 236 | 237 | /** 238 | * Convert key to PEM format for openssl functions 239 | * 240 | * @param string $key Key data. 241 | * 242 | * @return string 243 | */ 244 | public function key2pem( $key ) { 245 | return "-----BEGIN PUBLIC KEY-----\n" . chunk_split( $key, 64, "\n" ) . "-----END PUBLIC KEY-----\n"; 246 | } 247 | 248 | /** 249 | * Return RSA public key. 250 | * 251 | * @param bool $pem_format Wether to return the Key in PEM format. 252 | * @param bool $reset Wether to reset the private key. 253 | * 254 | * @return string 255 | * @throws Exception On Errors. 256 | */ 257 | public function get_public_key( $pem_format = false, $reset = false ) { 258 | $priv_key = $this->get_private_key( $reset ); 259 | 260 | $priv_key = openssl_pkey_get_private( $priv_key ); 261 | 262 | $public_key = openssl_pkey_get_details( $priv_key ); 263 | 264 | $public_key = $public_key['key']; 265 | 266 | if ( ! $pem_format ) { 267 | $public_key = str_replace( array( '-----BEGIN PUBLIC KEY-----', '-----END PUBLIC KEY-----', "\n" ), array( '', '', '' ), $public_key ); 268 | } 269 | 270 | return $public_key; 271 | } 272 | 273 | /** 274 | * Return RSA private key. 275 | * 276 | * @param bool $reset Wether to force reset the private key. 277 | * 278 | * @return string 279 | * @throws Exception On Errors. 280 | */ 281 | public function get_private_key( $reset = false ) { 282 | $private_key = get_option( self::KEYS_OPTION_PRIVATE_KEY, false ); 283 | 284 | if ( ( ! $private_key ) || $reset ) { 285 | $private_key = $this->generate_keys( false ); 286 | } 287 | 288 | return $private_key; 289 | } 290 | 291 | /** 292 | * From Incoming Exchange message we need to know which country belong the registration. 293 | * 294 | * @param array $headers Headers received. 295 | * 296 | * @return string 297 | */ 298 | protected function get_country_origin_from_header( $headers ) { 299 | switch ( $this->get_origin_header( $headers ) ) { 300 | case 'https://payments.amazon.com': 301 | return 'us'; 302 | case 'https://payments-eu.amazon.com': 303 | return 'eu'; 304 | default: 305 | return 'us'; 306 | } 307 | } 308 | 309 | /** 310 | * Check because getallheaders is only available for apache, we need a fallback in case of nginx or others, 311 | * http://php.net/manual/es/function.getallheaders.php 312 | * 313 | * @return array 314 | */ 315 | private function get_all_headers() { 316 | if ( ! function_exists( 'getallheaders' ) ) { 317 | $headers = array(); 318 | foreach ( $_SERVER as $name => $value ) { 319 | if ( substr( $name, 0, 5 ) === 'HTTP_' ) { 320 | $headers[ str_replace( ' ', '-', ucwords( strtolower( str_replace( '_', ' ', substr( $name, 5 ) ) ) ) ) ] = $value; 321 | } 322 | } 323 | return $headers; 324 | 325 | } else { 326 | return getallheaders(); 327 | } 328 | } 329 | 330 | /** 331 | * Apache uses capital, nginx uses not capitalised. 332 | * 333 | * @param array $headers Headers received. 334 | * 335 | * @return string 336 | */ 337 | private function get_origin_header( $headers ) { 338 | return ( $headers['Origin'] ) ? $headers['Origin'] : $headers['origin']; 339 | } 340 | 341 | /** 342 | * Get API Migration status. 343 | */ 344 | public static function get_migration_status() { 345 | $status = get_option( 'amazon_api_version' ); 346 | $old_install = version_compare( get_option( 'woocommerce_amazon_payments_new_install' ), '2.0.0', '>=' ); 347 | return 'V2' === $status || $old_install ? true : false; 348 | } 349 | 350 | /** 351 | * Update migration status update 352 | */ 353 | public static function update_migration_status() { 354 | update_option( 'amazon_api_version', 'V2' ); 355 | } 356 | 357 | /** 358 | * Downgrade migration status update 359 | */ 360 | public static function delete_migration_status() { 361 | delete_option( 'amazon_api_version' ); 362 | } 363 | 364 | } 365 | -------------------------------------------------------------------------------- /includes/class-wc-amazon-payments-advanced-utils.php: -------------------------------------------------------------------------------- 1 | get( \Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled() 26 | ? wc_get_page_screen_id( 'shop-order' ) 27 | : 'shop_order'; 28 | } 29 | 30 | 31 | /** 32 | * Get non required fields. 33 | * 34 | * @return array 35 | */ 36 | public static function get_non_required_fields() { 37 | 38 | $non_required_fields = array( 39 | 'billing_last_name', 40 | 'billing_state', 41 | 'billing_phone', 42 | 'shipping_last_name', 43 | 'shipping_state', 44 | ); 45 | 46 | return apply_filters( 'woocommerce_amazon_pa_non_required_fields', $non_required_fields ); 47 | } 48 | 49 | 50 | /** 51 | * Get non required fields per country. 52 | * 53 | * @return array 54 | */ 55 | public static function get_non_required_fields_per_country() { 56 | 57 | $mapped_fields_per_country = array( 58 | 'JP' => array( 59 | 'city', 60 | ), 61 | ); 62 | 63 | $non_required_fields = array(); 64 | 65 | foreach ( $mapped_fields_per_country as $country => $fields ) { 66 | foreach ( $fields as $field ) { 67 | $non_required_fields[ $country ][] = 'billing_' . $field; 68 | $non_required_fields[ $country ][] = 'billing-' . $field; 69 | $non_required_fields[ $country ][] = 'shipping_' . $field; 70 | $non_required_fields[ $country ][] = 'shipping-' . $field; 71 | } 72 | } 73 | 74 | return apply_filters( 'woocommerce_amazon_pa_non_required_fields_per_country', $non_required_fields, $mapped_fields_per_country ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /includes/class-wc-gateway-amazon-payments-advanced-express.php: -------------------------------------------------------------------------------- 1 | method_title = __( 'Amazon Pay Express', 'woocommerce-gateway-amazon-payments-advanced' ); 37 | $this->method_description = __( 'Amazon Pay is embedded directly into your existing web site, and all the buyer interactions with Amazon Pay and Login with Amazon take place in embedded widgets so that the buyer never leaves your site. Buyers can log in using their Amazon account, select a shipping address and payment method, and then confirm their order. Requires an Amazon Pay seller account and supports USA, UK, Germany, France, Italy, Spain, Luxembourg, the Netherlands, Sweden, Portugal, Hungary, Denmark, and Japan.', 'woocommerce-gateway-amazon-payments-advanced' ); 38 | $this->icon = apply_filters( 'woocommerce_amazon_pa_logo', wc_apa()->plugin_url . '/build/images/amazon-payments.png' ); 39 | $this->view_transaction_url = $this->get_transaction_url_format(); 40 | $this->supports = array( 41 | 'products', 42 | 'refunds', 43 | ); 44 | $this->supports = apply_filters( 'woocommerce_amazon_pa_supports', $this->supports, wc_apa()->get_gateway() ); 45 | $this->private_key = get_option( WC_Amazon_Payments_Advanced_Merchant_Onboarding_Handler::KEYS_OPTION_PRIVATE_KEY ); 46 | 47 | $this->settings = WC_Amazon_Payments_Advanced_API::get_settings(); 48 | $this->load_settings(); 49 | add_action( 'admin_enqueue_scripts', array( __CLASS__, 'visually_hide_amazon_express_on_backend' ) ); 50 | add_action( 'woocommerce_amazon_pa_processed_order', array( $this, 'update_orders_payment_method' ), 1 ); 51 | add_action( 'woocommerce_store_api_checkout_update_order_meta', array( $this, 'available_on_block_checkout' ) ); 52 | add_action( 'woocommerce_checkout_init', array( $this, 'restore_amazon_payments_advanced' ), 30 ); 53 | } 54 | 55 | /** 56 | * Skip un-setting amazon_payments_advanced gateway when using WooCommerce Blocks Checkout. 57 | * 58 | * @return void 59 | */ 60 | public function restore_amazon_payments_advanced() { 61 | if ( $this->using_woo_blocks() ) { 62 | remove_filter( 'woocommerce_available_payment_gateways', array( wc_apa()->get_gateway(), 'remove_amazon_gateway' ) ); 63 | } 64 | } 65 | 66 | /** 67 | * Make the gateway available when checkout triggered through WooCommerce Blocks. 68 | * 69 | * @return void 70 | */ 71 | public function available_on_block_checkout() { 72 | $this->available = true; 73 | } 74 | 75 | /** 76 | * Returns Amazon Pay Express availability. 77 | * 78 | * @return bool 79 | */ 80 | protected function get_availability() { 81 | return parent::get_availability() && $this->available; 82 | } 83 | 84 | /** 85 | * Returns an empty array as the Gateway's settings. 86 | * 87 | * @return array 88 | */ 89 | public function get_form_fields() { 90 | return array(); 91 | } 92 | 93 | /** 94 | * Update a single option. 95 | * 96 | * @param string $key Option key. 97 | * @param mixed $value Value to set. 98 | * @return bool was anything saved? 99 | */ 100 | public function update_option( $key, $value = '' ) { 101 | return true; 102 | } 103 | 104 | /** 105 | * Visually hides the gateway from the Backend list of available gateways. 106 | * 107 | * @return void 108 | */ 109 | public static function visually_hide_amazon_express_on_backend() { 110 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 111 | wp_enqueue_style( 'amazon_payments_advanced_hide_express', wc_apa()->plugin_url . '/build/css/hide-amazon-express-admin' . $min . '.css', array(), wc_apa()->version ); 112 | } 113 | 114 | /** 115 | * Change the payment method after order is completed. 116 | * 117 | * @param WC_Order $order The order being completed. 118 | * @return void 119 | */ 120 | public function update_orders_payment_method( $order ) { 121 | if ( $this->id === $order->get_payment_method() ) { 122 | $order->set_payment_method( wc_apa()->get_gateway() ); 123 | $order->save(); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /includes/class-wc-gateway-amazon-payments-advanced-privacy.php: -------------------------------------------------------------------------------- 1 | add_exporter( 'woocommerce-gateway-amazon-payments-advanced-order-data', __( 'WooCommerce Amazon Pay Order Data', 'woocommerce-gateway-amazon-payments-advanced' ), array( $this, 'order_data_exporter' ) ); 23 | 24 | if ( function_exists( 'wcs_get_subscriptions' ) ) { 25 | $this->add_exporter( 'woocommerce-gateway-amazon-payments-advanced-subscriptions-data', __( 'WooCommerce Amazon Pay Subscriptions Data', 'woocommerce-gateway-amazon-payments-advanced' ), array( $this, 'subscriptions_data_exporter' ) ); 26 | } 27 | 28 | $this->add_eraser( 'woocommerce-gateway-amazon-payments-advanced-order-data', __( 'WooCommerce Amazon Pay Data', 'woocommerce-gateway-amazon-payments-advanced' ), array( $this, 'order_data_eraser' ) ); 29 | } 30 | 31 | /** 32 | * Returns a list of orders that are using one of Amazon's payment methods. 33 | * 34 | * @param string $email_address Email address to search orders for. 35 | * @param int $page Page being processed. 36 | * 37 | * @return array WP_Post 38 | */ 39 | protected function get_amazon_orders( $email_address, $page ) { 40 | $user = get_user_by( 'email', $email_address ); // Check if user has an ID in the DB to load stored personal data. 41 | 42 | $order_query = array( 43 | 'payment_method' => 'amazon_payments_advanced', 44 | 'limit' => 10, 45 | 'page' => $page, 46 | ); 47 | 48 | if ( $user instanceof WP_User ) { 49 | $order_query['customer_id'] = (int) $user->ID; 50 | } else { 51 | $order_query['billing_email'] = $email_address; 52 | } 53 | 54 | return wc_get_orders( $order_query ); 55 | } 56 | 57 | /** 58 | * Gets the message of the privacy to display. 59 | */ 60 | public function get_privacy_message() { 61 | /* translators: 1) URL to privacy page. */ 62 | return wpautop( sprintf( __( 'By using this extension, you may be storing personal data or sharing data with an external service. Learn more about how this works, including what you may want to include in your privacy policy.', 'woocommerce-gateway-amazon-payments-advanced' ), 'https://docs.woocommerce.com/document/privacy-payments/#woocommerce-gateway-amazon-payments-advanced' ) ); 63 | } 64 | 65 | /** 66 | * Handle exporting data for Orders. 67 | * 68 | * @param string $email_address E-mail address to export. 69 | * @param int $page Pagination of data. 70 | * 71 | * @return array 72 | */ 73 | public function order_data_exporter( $email_address, $page = 1 ) { 74 | $done = false; 75 | $data_to_export = array(); 76 | 77 | $orders = $this->get_amazon_orders( $email_address, (int) $page ); 78 | 79 | $done = true; 80 | 81 | if ( 0 < count( $orders ) ) { 82 | foreach ( $orders as $order ) { 83 | $data_to_export[] = array( 84 | 'group_id' => 'woocommerce_orders', 85 | 'group_label' => __( 'Orders', 'woocommerce-gateway-amazon-payments-advanced' ), 86 | 'item_id' => 'order-' . $order->get_id(), 87 | 'data' => array( 88 | array( 89 | 'name' => __( 'Amazon Pay authorization id', 'woocommerce-gateway-amazon-payments-advanced' ), 90 | 'value' => $order->get_meta( 'amazon_authorization_id', true, 'edit' ), 91 | ), 92 | array( 93 | 'name' => __( 'Amazon Pay capture id', 'woocommerce-gateway-amazon-payments-advanced' ), 94 | 'value' => $order->get_meta( 'amazon_capture_id', true, 'edit' ), 95 | ), 96 | array( 97 | 'name' => __( 'Amazon Pay reference id', 'woocommerce-gateway-amazon-payments-advanced' ), 98 | 'value' => $order->get_meta( 'amazon_reference_id', true, 'edit' ), 99 | ), 100 | array( 101 | 'name' => __( 'Amazon Pay refunds id', 'woocommerce-gateway-amazon-payments-advanced' ), 102 | 'value' => wp_json_encode( $order->get_meta( 'amazon_refund_id', false, 'edit' ) ), 103 | ), 104 | array( 105 | 'name' => __( 'Amazon subscription token', 'woocommerce-gateway-amazon-payments-advanced' ), 106 | 'value' => $order->get_meta( 'amazon_billing_agreement_id', true, 'edit' ), 107 | ), 108 | array( 109 | 'name' => __( 'Amazon Pay charge permission id', 'woocommerce-gateway-amazon-payments-advanced' ), 110 | 'value' => WC_Amazon_Payments_Advanced::get_order_charge_permission( $order->get_id() ), 111 | ), 112 | array( 113 | 'name' => __( 'Amazon Pay charge id', 'woocommerce-gateway-amazon-payments-advanced' ), 114 | 'value' => WC_Amazon_Payments_Advanced::get_order_charge_id( $order->get_id() ), 115 | ), 116 | ), 117 | ); 118 | } 119 | 120 | $done = 10 > count( $orders ); 121 | } 122 | 123 | return array( 124 | 'data' => $data_to_export, 125 | 'done' => $done, 126 | ); 127 | } 128 | 129 | /** 130 | * Handle exporting data for Subscriptions. 131 | * 132 | * @param string $email_address E-mail address to export. 133 | * @param int $page Pagination of data. 134 | * 135 | * @return array 136 | */ 137 | public function subscriptions_data_exporter( $email_address, $page = 1 ) { 138 | $done = false; 139 | $page = (int) $page; 140 | $data_to_export = array(); 141 | 142 | $meta_query = array( 143 | 'relation' => 'AND', 144 | array( 145 | 'key' => '_payment_method', 146 | 'value' => 'amazon_payments_advanced', 147 | 'compare' => '=', 148 | ), 149 | array( 150 | 'key' => '_billing_email', 151 | 'value' => $email_address, 152 | 'compare' => '=', 153 | ), 154 | ); 155 | 156 | $subscription_query = array( 157 | 'posts_per_page' => 10, 158 | 'page' => $page, 159 | 'meta_query' => $meta_query, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query 160 | ); 161 | 162 | $subscriptions = wcs_get_subscriptions( $subscription_query ); 163 | 164 | $done = true; 165 | 166 | if ( 0 < count( $subscriptions ) ) { 167 | foreach ( $subscriptions as $subscription ) { 168 | $data_to_export[] = array( 169 | 'group_id' => 'woocommerce_subscriptions', 170 | 'group_label' => __( 'Subscriptions', 'woocommerce-gateway-amazon-payments-advanced' ), 171 | 'item_id' => 'subscription-' . $subscription->get_id(), 172 | 'data' => array( 173 | array( 174 | 'name' => __( 'Amazon subscription token', 'woocommerce-gateway-amazon-payments-advanced' ), 175 | 'value' => $subscription->get_meta( 'amazon_billing_agreement_id', true, 'true' ), 176 | ), 177 | array( 178 | 'name' => __( 'Amazon Pay charge permission id', 'woocommerce-gateway-amazon-payments-advanced' ), 179 | 'value' => WC_Amazon_Payments_Advanced::get_order_charge_permission( $subscription->get_id() ), 180 | ), 181 | ), 182 | ); 183 | } 184 | 185 | $done = 10 > count( $subscriptions ); 186 | } 187 | 188 | return array( 189 | 'data' => $data_to_export, 190 | 'done' => $done, 191 | ); 192 | } 193 | 194 | /** 195 | * Finds and erases order data by email address. 196 | * 197 | * @since 3.4.0 198 | * @param string $email_address The user email address. 199 | * @param int $page Page. 200 | * @return array An array of personal data in name value pairs 201 | */ 202 | public function order_data_eraser( $email_address, $page ) { 203 | $orders = $this->get_amazon_orders( $email_address, (int) $page ); 204 | 205 | $items_removed = false; 206 | $items_retained = false; 207 | $messages = array(); 208 | 209 | foreach ( (array) $orders as $order ) { 210 | $order = wc_get_order( $order->get_id() ); 211 | 212 | $refunds = $order->get_refunds(); 213 | foreach ( $refunds as $refund ) { 214 | list( $removed, $retained, $msgs ) = $this->maybe_handle_order( $refund ); 215 | $items_removed |= $removed; 216 | $items_retained |= $retained; 217 | $messages = array_merge( $messages, $msgs ); 218 | } 219 | 220 | list( $removed, $retained, $msgs ) = $this->maybe_handle_subscription( $order ); 221 | $items_removed |= $removed; 222 | $items_retained |= $retained; 223 | $messages = array_merge( $messages, $msgs ); 224 | 225 | list( $removed, $retained, $msgs ) = $this->maybe_handle_order( $order ); 226 | $items_removed |= $removed; 227 | $items_retained |= $retained; 228 | $messages = array_merge( $messages, $msgs ); 229 | } 230 | 231 | // Tell core if we have more orders to work on still. 232 | $done = count( $orders ) < 10; 233 | 234 | return array( 235 | 'items_removed' => $items_removed, 236 | 'items_retained' => $items_retained, 237 | 'messages' => $messages, 238 | 'done' => $done, 239 | ); 240 | } 241 | 242 | /** 243 | * Handle eraser of data tied to Subscriptions 244 | * 245 | * @param WC_Order $order Order object. 246 | * @return array 247 | */ 248 | protected function maybe_handle_subscription( $order ) { 249 | if ( ! class_exists( 'WC_Subscriptions' ) ) { 250 | return array( false, false, array() ); 251 | } 252 | 253 | if ( ! wcs_order_contains_subscription( $order ) ) { 254 | return array( false, false, array() ); 255 | } 256 | 257 | $subscription = current( wcs_get_subscriptions_for_order( $order->get_id() ) ); 258 | $subscription_id = $subscription->get_id(); 259 | 260 | if ( $subscription->has_status( apply_filters( 'wc_amazon_pay_privacy_eraser_subs_statuses', array( 'on-hold', 'active' ) ) ) ) { 261 | /* translators: 1) Subscription ID. */ 262 | return array( false, true, array( sprintf( __( 'Amazon Payments Advanced data within subscription %1$s has been retained because it is an active Subscription. ', 'woocommerce-gateway-amazon-payments-advanced' ), $subscription_id ) ) ); 263 | } 264 | 265 | return $this->maybe_handle_order( $subscription ); 266 | } 267 | 268 | /** 269 | * Handle eraser of data tied to Orders 270 | * 271 | * @param WC_Order $order Order object. 272 | * @return array 273 | */ 274 | protected function maybe_handle_order( $order ) { 275 | $meta_to_delete = array( 276 | 'amazon_authorization_id', 277 | 'amazon_authorization_state', 278 | 'amazon_capture_id', 279 | 'amazon_capture_state', 280 | 'amazon_reference_id', 281 | 'amazon_reference_state', 282 | 'amazon_refund_id', 283 | 'amazon_refunds', 284 | 'amazon_billing_agreement_id', 285 | 'amazon_billing_agreement_state', 286 | 'amazon_charge_permission_id', 287 | 'amazon_charge_permission_status', 288 | 'amazon_charge_id', 289 | 'amazon_charge_status', 290 | ); 291 | 292 | $deleted = false; 293 | foreach ( $meta_to_delete as $key ) { 294 | $meta_value = $order->get_meta( $key, true, 'edit' ); 295 | if ( empty( $meta_value ) ) { 296 | continue; 297 | } 298 | $order->delete_meta_data( $key ); 299 | $deleted = true; 300 | } 301 | 302 | $order->save(); 303 | 304 | $messages = array(); 305 | if ( $deleted ) { 306 | $type = __( 'order', 'woocommerce-gateway-amazon-payments-advanced' ); 307 | if ( 'shop_subscription' === $order->get_type() ) { 308 | $type = __( 'subscription', 'woocommerce-gateway-amazon-payments-advanced' ); 309 | } 310 | if ( 'shop_order_refund' === $order->get_type() ) { 311 | $type = __( 'refund', 'woocommerce-gateway-amazon-payments-advanced' ); 312 | } 313 | /* translators: 1) Object ID 2) Object Type. */ 314 | $messages = array( sprintf( __( 'Amazon Payments Advanced data within %2$s %1$s has been removed.', 'woocommerce-gateway-amazon-payments-advanced' ), $order->get_id(), $type ) ); 315 | } 316 | 317 | return array( $deleted, false, $messages ); 318 | } 319 | } 320 | 321 | new WC_Gateway_Amazon_Payments_Advanced_Privacy(); 322 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-drip-compat.php: -------------------------------------------------------------------------------- 1 | plugin_url . '/build/js/non-block/amazon-wcdrip-compat' . $js_suffix; 32 | wp_enqueue_script( 'amazon_pa_drip_compat', $url, array( 'amazon_payments_advanced' ), wc_apa()->version, true ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-dynamic-pricing-compat.php: -------------------------------------------------------------------------------- 1 | needs_payment(). 37 | // becomes `false`. 38 | if ( false === $cart->subtotal && $cart->total > 0 ) { 39 | add_filter( 'woocommerce_cart_needs_payment', '__return_true' ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-multi-currency-abstract.php: -------------------------------------------------------------------------------- 1 | is_front_end_compatible() ) { 27 | return; 28 | } 29 | 30 | $version = is_a( wc_apa()->get_gateway(), 'WC_Gateway_Amazon_Payments_Advanced_Legacy' ) ? 'v1' : 'v2'; 31 | if ( 'v1' === $version ) { 32 | // Add AJAX call to retrieve current currency on frontend. 33 | add_action( 'wp_ajax_amazon_get_currency', array( $this, 'ajax_get_currency' ) ); 34 | add_action( 'wp_ajax_nopriv_amazon_get_currency', array( $this, 'ajax_get_currency' ) ); 35 | } 36 | 37 | // Currency switching observer. 38 | add_action( 'woocommerce_amazon_checkout_init', array( $this, 'capture_original_checkout_currency' ) ); 39 | add_action( 'woocommerce_thankyou_amazon_payments_advanced', array( $this, 'delete_currency_session' ) ); 40 | add_action( 'woocommerce_amazon_pa_logout', array( $this, 'delete_currency_session' ) ); 41 | 42 | add_action( 'woocommerce_init', array( $this, 'maybe_disable_due_to_unsupported_currency' ), 10000 ); 43 | } 44 | 45 | /** 46 | * After WC and relevant multicurrency plugins have initialized 47 | * 48 | * @return void 49 | */ 50 | public function maybe_disable_due_to_unsupported_currency() { 51 | // If selected currency is not compatible with Amazon. 52 | if ( ! $this->is_currency_compatible( static::get_active_currency() ) ) { 53 | add_filter( 'woocommerce_amazon_payments_init', '__return_false' ); 54 | return; 55 | } 56 | 57 | add_filter( 'woocommerce_amazon_pa_create_checkout_session_params', array( $this, 'set_presentment_currency' ) ); 58 | add_filter( 'woocommerce_amazon_pa_create_checkout_session_classic_params', array( $this, 'set_presentment_currency' ) ); 59 | } 60 | 61 | /** 62 | * Get selected currency function. 63 | * 64 | * @deprecated 2.1.2 65 | * 66 | * @abstract Used to be abstract up to version 2.1.1. Has been replaced by get_active_currency. 67 | * 68 | * @return string 69 | */ 70 | public function get_selected_currency() { 71 | _deprecated_function( __METHOD__, '2.1.2', 'WC_Amazon_Payments_Advanced_Multi_Currency_Abstract::get_active_currency' ); 72 | return static::get_active_currency(); 73 | } 74 | 75 | /** 76 | * Interface for get active currency function 77 | * 78 | * @abstract It will be made abstract in future release, in order to give time to merchants extending this class to adjust their code. 79 | * 80 | * @since 2.1.2 81 | * 82 | * @return string 83 | */ 84 | public static function get_active_currency() { 85 | return get_woocommerce_currency(); 86 | } 87 | 88 | /** 89 | * If Multi-currency plugin is frontend compatible, meaning all currency changes happens only on frontend level. 90 | * 91 | * @return bool 92 | */ 93 | public function is_front_end_compatible() { 94 | return false; 95 | } 96 | 97 | /** 98 | * Check if the $currency_selected is compatible with amazon (and has been selected on settings). 99 | * 100 | * @param string $currency_selected Current currency selected from the frontend. 101 | * 102 | * @return bool 103 | */ 104 | public function is_currency_compatible( $currency_selected ) { 105 | $amazon_selected_currencies = WC_Amazon_Payments_Advanced_API::get_selected_currencies(); 106 | return ( false !== ( array_search( $currency_selected, $amazon_selected_currencies, true ) ) ); 107 | } 108 | 109 | /** 110 | * Capture original currency used on checkout and save it to Session. 111 | */ 112 | public function capture_original_checkout_currency() { 113 | $original_currency = WC()->session->get( self::ORIGINAL_CURRENCY_SESSION ); 114 | 115 | if ( ! $original_currency ) { 116 | WC()->session->set( self::ORIGINAL_CURRENCY_SESSION, static::get_active_currency() ); 117 | WC()->session->set( self::CURRENCY_TIMES_SWITCHED_SESSION, 0 ); 118 | } else { 119 | // Only increase once, on ajax checkout render. 120 | if ( is_ajax() ) { 121 | $switched_times = WC()->session->get( self::CURRENCY_TIMES_SWITCHED_SESSION ); 122 | WC()->session->set( self::CURRENCY_TIMES_SWITCHED_SESSION, ( $switched_times + 1 ) ); 123 | } 124 | } 125 | } 126 | 127 | /** 128 | * Triggered on thank you hook, it will delete currency session. 129 | */ 130 | public function delete_currency_session() { 131 | WC()->session->__unset( self::ORIGINAL_CURRENCY_SESSION ); 132 | WC()->session->__unset( self::CURRENCY_TIMES_SWITCHED_SESSION ); 133 | WC()->session->__unset( self::CURRENCY_BYPASS_SESSION ); 134 | } 135 | 136 | /** 137 | * Get amount of times currencies have been changed. 138 | * 139 | * @return string 140 | */ 141 | public function get_currency_switched_times() { 142 | return ( WC()->session->get( self::CURRENCY_BYPASS_SESSION ) ) ? 0 : WC()->session->get( self::CURRENCY_TIMES_SWITCHED_SESSION ); 143 | } 144 | 145 | /** 146 | * Set presentmentCurrency on the payment details 147 | * 148 | * @param array $payload Payload on the checkout session object. 149 | * @return array 150 | */ 151 | public function set_presentment_currency( $payload ) { 152 | if ( ! isset( $payload['paymentDetails'] ) ) { 153 | $payload['paymentDetails'] = array(); 154 | } 155 | 156 | $payload['paymentDetails']['presentmentCurrency'] = static::get_active_currency(); 157 | 158 | return $payload; 159 | } 160 | 161 | /** 162 | * LEGACY v1 METHODS AND HOOKS 163 | */ 164 | 165 | /** 166 | * Option to bypass currency session. 167 | * This will be triggered on order reference statuses equal to pending, where is not allowed switching multicurrency. 168 | */ 169 | public function bypass_currency_session() { 170 | WC()->session->set( self::CURRENCY_BYPASS_SESSION, true ); 171 | } 172 | 173 | /** 174 | * Get original currency session. 175 | * 176 | * @return string 177 | */ 178 | public function get_original_checkout_currency() { 179 | return WC()->session->get( self::ORIGINAL_CURRENCY_SESSION ); 180 | } 181 | 182 | /** 183 | * Flag if we need to reload Amazon wallet on frontend. 184 | * 185 | * @return bool 186 | */ 187 | public function reload_wallet_widget() { 188 | return false; 189 | } 190 | 191 | /** 192 | * Get selected currency, to be used on frontend. 193 | */ 194 | public function ajax_get_currency() { 195 | check_ajax_referer( 'multi_currency_nonce', 'nonce' ); 196 | echo esc_html( static::get_active_currency() ); 197 | wp_die(); 198 | } 199 | 200 | /** 201 | * If current order reference on checkout, after invalid payment method for instance, check if status is Suspended. 202 | * 203 | * @return bool 204 | */ 205 | public function is_order_reference_checkout_suspended() { 206 | if ( ! defined( 'DOING_AJAX' ) && isset( WC()->session->amazon_reference_id ) && WC()->session->order_awaiting_payment > 0 ) { 207 | $order_awaiting_payment = WC()->session->order_awaiting_payment; 208 | 209 | // Sometimes called in a hook to soon. 210 | if ( did_action( 'woocommerce_after_register_post_type' ) ) { 211 | $order = wc_get_order( $order_awaiting_payment ); 212 | $amazon_reference_state = $order->get_meta( 'amazon_reference_state' ); 213 | $amazon_reference_id = $order->get_meta( 'amazon_reference_id' ); 214 | } else { 215 | $amazon_reference_state = get_post_meta( $order_awaiting_payment, 'amazon_reference_state', true ); 216 | $amazon_reference_id = get_post_meta( $order_awaiting_payment, 'amazon_reference_id', true ); 217 | } 218 | 219 | // If amazon_reference_id order_awaiting_payment's order is not the current amazon_reference_id on session, bail out. 220 | if ( WC()->session->amazon_reference_id !== $amazon_reference_id ) { 221 | return false; 222 | } 223 | 224 | if ( 'suspended' === strtolower( $amazon_reference_state ) ) { 225 | return true; 226 | } 227 | 228 | $amazon_authorization_state = WC_Amazon_Payments_Advanced_API_Legacy::get_reference_state( $order_awaiting_payment, $amazon_reference_id ); 229 | if ( 'suspended' === strtolower( $amazon_authorization_state ) ) { 230 | return true; 231 | } 232 | } 233 | return false; 234 | } 235 | } 236 | 237 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-multi-currency-ppbc.php: -------------------------------------------------------------------------------- 1 | get_gateway(), 'WC_Gateway_Amazon_Payments_Advanced_Legacy' ) ? 'v1' : 'v2'; 32 | if ( 'v1' === $version ) { 33 | // Hooks before PPBC inits to inject new zone if needed. 34 | add_action( 'wc_price_based_country_before_frontend_init', array( $this, 'hook_before_ppbc_update_order_review' ) ); 35 | add_action( 'wc_price_based_country_before_frontend_init', array( $this, 'hook_before_ppbc_get_refreshed_fragments' ) ); 36 | 37 | add_action( 'widgets_init', array( $this, 'remove_currency_switcher_on_order_reference_suspended' ) ); 38 | } 39 | 40 | $this->ppbc = WC_Product_Price_Based_Country::instance(); 41 | parent::__construct(); 42 | } 43 | 44 | /** 45 | * Get PPBC selected currency. 46 | * 47 | * @return string 48 | */ 49 | public static function get_active_currency() { 50 | // This is for sandbox mode, changing countries manually. 51 | // phpcs:disable WordPress.Security.NonceVerification.Recommended 52 | if ( isset( $_REQUEST['wcpbc-manual-country'] ) ) { 53 | $manual_country = wc_clean( wp_unslash( $_REQUEST['wcpbc-manual-country'] ) ); 54 | $selected_zone = WCPBC_Pricing_Zones::get_zone_by_country( $manual_country ); 55 | } else { 56 | $selected_zone = wcpbc_get_zone_by_country(); 57 | } 58 | // phpcs:enable WordPress.Security.NonceVerification.Recommended 59 | return ( $selected_zone ) ? $selected_zone->get_currency() : get_woocommerce_currency(); 60 | } 61 | 62 | /** 63 | * LEGACY v1 METHODS AND HOOKS 64 | */ 65 | 66 | /** 67 | * Get selected currency, to be used on frontend. 68 | */ 69 | public function ajax_get_currency() { 70 | check_ajax_referer( 'multi_currency_nonce', 'nonce' ); 71 | if ( $this->is_currency_compatible( self::get_active_currency() ) ) { 72 | $currency = self::get_active_currency(); 73 | } else { 74 | $currency = wcpbc_get_base_currency(); 75 | } 76 | echo esc_html( $currency ); 77 | wp_die(); 78 | } 79 | 80 | /** 81 | * As Changing addresses on Address widget changes currency, we need to reload wallet accordingly. 82 | * 83 | * @return bool 84 | */ 85 | public function reload_wallet_widget() { 86 | return true; 87 | } 88 | 89 | /** 90 | * Allow PPBC to get proper country every time user changes its address on address widget. 91 | * If the new shipping address has associated a currency amazon does not support, set base country to session, then new currency is not force. 92 | * Hooks before $ppbc looks for Customer Session. 93 | * Amazon has not set up yet the customer information, so we need to set shipping and billing. 94 | */ 95 | public function hook_before_ppbc_update_order_review() { 96 | if ( defined( 'WC_DOING_AJAX' ) && 97 | WC_DOING_AJAX && 98 | // phpcs:disable WordPress.Security.NonceVerification.Recommended 99 | isset( $_GET['wc-ajax'] ) && 100 | 'update_order_review' === $_GET['wc-ajax'] && 101 | isset( $_REQUEST['payment_method'] ) && 102 | 'amazon_payments_advanced' === $_REQUEST['payment_method'] 103 | // phpcs:enable WordPress.Security.NonceVerification.Recommended 104 | ) { 105 | $order_details = $this->get_amazon_order_details(); 106 | // @codingStandardsIgnoreStart 107 | if ( ! $order_details || ! isset( $order_details->Destination->PhysicalDestination ) ) { 108 | return; 109 | } 110 | 111 | $address = WC_Amazon_Payments_Advanced_API::format_address( $order_details->Destination->PhysicalDestination ); 112 | // @codingStandardsIgnoreEnd 113 | if ( isset( $address['country'] ) ) { 114 | 115 | $ppbc_zone = WCPBC_Pricing_Zones::get_zone_by_country( $address['country'] ); 116 | 117 | /** 118 | * If zone not defined, fallback zone handled by PPCB 119 | */ 120 | if ( ! $ppbc_zone ) { 121 | $this->set_shipping_billing_customer( $address['country'] ); 122 | return; 123 | } 124 | 125 | $currency_selected_zone = $ppbc_zone->get_currency(); 126 | 127 | if ( $this->is_currency_compatible( $currency_selected_zone ) ) { 128 | $this->set_shipping_billing_customer( $address['country'] ); 129 | } else { 130 | // If currency not compatible with Amazon, we set woo base country. 131 | $base_country = $this->get_base_country(); 132 | 133 | $this->set_shipping_billing_customer( $base_country ); 134 | WC()->session->set( self::FORCE_NEW_ZONE_SESSION, $base_country ); 135 | } 136 | } 137 | } 138 | } 139 | 140 | /** 141 | * If the country has been forced on hook_before_ppbc_update_order_review, set session again so 142 | * get_refreshed_fragments gets same country/currency. 143 | */ 144 | public function hook_before_ppbc_get_refreshed_fragments() { 145 | if ( defined( 'WC_DOING_AJAX' ) && 146 | WC_DOING_AJAX && 147 | // phpcs:disable WordPress.Security.NonceVerification.Recommended 148 | isset( $_GET['wc-ajax'] ) && 149 | 'get_refreshed_fragments' === $_GET['wc-ajax'] 150 | // phpcs:enable WordPress.Security.NonceVerification.Recommended 151 | ) { 152 | $base_country = WC()->session->get( self::FORCE_NEW_ZONE_SESSION ); 153 | if ( $base_country ) { 154 | $this->set_shipping_billing_customer( $base_country ); 155 | WC()->session->__unset( self::FORCE_NEW_ZONE_SESSION ); 156 | } 157 | } 158 | } 159 | 160 | /** 161 | * Get base country where the shop is set up. 162 | * 163 | * @return string 164 | */ 165 | public function get_base_country() { 166 | $base_location = wc_get_base_location(); 167 | return $base_location['country']; 168 | } 169 | 170 | /** 171 | * Sets billing and shipping country on WC customer. 172 | * 173 | * @param string $country 174 | */ 175 | public function set_shipping_billing_customer( $country ) { 176 | WC()->customer->set_shipping_country( $country ); 177 | WC()->customer->set_billing_country( $country ); 178 | } 179 | 180 | /** 181 | * Get Amazon Order Details from current Reference id. 182 | * 183 | * @return bool|SimpleXMLElement 184 | */ 185 | public function get_amazon_order_details() { 186 | 187 | $request_args = array( 188 | 'Action' => 'GetOrderReferenceDetails', 189 | 'AmazonOrderReferenceId' => WC_Amazon_Payments_Advanced_API_Legacy::get_reference_id(), 190 | ); 191 | 192 | /** 193 | * Full address information is available to the 'GetOrderReferenceDetails' call when we're in 194 | * "login app" mode and we pass the AddressConsentToken to the API. 195 | * 196 | * @see the "Getting the Shipping Address" section here: https://payments.amazon.com/documentation/lpwa/201749990 197 | */ 198 | $settings = WC_Amazon_Payments_Advanced_API::get_settings(); 199 | if ( 'yes' === $settings['enable_login_app'] ) { 200 | $request_args['AddressConsentToken'] = WC_Amazon_Payments_Advanced_API_Legacy::get_access_token(); 201 | } 202 | 203 | $response = WC_Amazon_Payments_Advanced_API_Legacy::request( $request_args ); 204 | 205 | // @codingStandardsIgnoreStart 206 | if ( ! is_wp_error( $response ) && isset( $response->GetOrderReferenceDetailsResult->OrderReferenceDetails ) ) { 207 | return $response->GetOrderReferenceDetailsResult->OrderReferenceDetails; 208 | } 209 | // @codingStandardsIgnoreEnd 210 | 211 | return false; 212 | } 213 | 214 | /** 215 | * On OrderReferenceStatus === Suspended, hide currency switcher. 216 | */ 217 | public function remove_currency_switcher_on_order_reference_suspended() { 218 | if ( $this->is_order_reference_checkout_suspended() ) { 219 | // By Pass Multi-currency, so we don't trigger a new set_order_reference_details on process_payment. 220 | $this->bypass_currency_session(); 221 | unregister_widget( 'WCPBC_Widget_Country_Selector' ); 222 | } 223 | } 224 | 225 | } 226 | 227 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-multi-currency-wccw.php: -------------------------------------------------------------------------------- 1 | get_gateway(), 'WC_Gateway_Amazon_Payments_Advanced_Legacy' ) ? 'v1' : 'v2'; 21 | if ( 'v1' === $version ) { 22 | // Option woocs_restrike_on_checkout_page === 1 will hide switcher on checkout. 23 | add_filter( 'option_woocs_restrike_on_checkout_page', array( $this, 'remove_currency_switcher_on_order_reference_suspended' ) ); 24 | add_action( 'init', array( $this, 'remove_shortcode_currency_switcher_on_order_reference_suspended' ) ); 25 | } 26 | 27 | parent::__construct(); 28 | } 29 | 30 | 31 | /** 32 | * Get Woocs selected currency. 33 | * 34 | * @return string 35 | */ 36 | public static function get_active_currency() { 37 | global $WOOCS; // phpcs:ignore WordPress.NamingConventions 38 | return is_object( $WOOCS ) && ! empty( $WOOCS->current_currency ) ? $WOOCS->current_currency : get_woocommerce_currency(); // phpcs:ignore WordPress.NamingConventions 39 | } 40 | 41 | /** 42 | * Woocs has 2 ways of work: 43 | * Settings > Advanced > Is multiple allowed 44 | * If it is set, users will pay on selected currency (where we hook) 45 | * otherwise it will just change currency on frontend, but order will be taken on original shop currency. 46 | * 47 | * @return bool 48 | */ 49 | public function is_front_end_compatible() { 50 | return get_option( 'woocs_is_multiple_allowed' ) ? false : true; 51 | } 52 | 53 | /** 54 | * LEGACY v1 METHODS AND HOOKS 55 | */ 56 | 57 | /** 58 | * On OrderReferenceStatus === Suspended, hide currency switcher. 59 | * 60 | * @param bool $value Wether to remove or not the switcher. 61 | * @return bool 62 | */ 63 | public function remove_currency_switcher_on_order_reference_suspended( $value ) { 64 | if ( $this->is_order_reference_checkout_suspended() ) { 65 | // By Pass Multi-currency, so we don't trigger a new set_order_reference_details on process_payment. 66 | $this->bypass_currency_session(); 67 | return 1; 68 | } 69 | return $value; 70 | } 71 | 72 | /** 73 | * On OrderReferenceStatus === Suspended, hide currency switcher. 74 | */ 75 | public function remove_shortcode_currency_switcher_on_order_reference_suspended() { 76 | if ( $this->is_order_reference_checkout_suspended() ) { 77 | // By Pass Multi-currency, so we don't trigger a new set_order_reference_details on process_payment. 78 | $this->bypass_currency_session(); 79 | remove_shortcode( 'woocs' ); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-multi-currency-wpml.php: -------------------------------------------------------------------------------- 1 | wpml = $woocommerce_wpml; 28 | 29 | $version = is_a( wc_apa()->get_gateway(), 'WC_Gateway_Amazon_Payments_Advanced_Legacy' ) ? 'v1' : 'v2'; 30 | if ( 'v1' === $version ) { 31 | add_filter( 'init', array( $this, 'remove_currency_switcher_on_order_reference_suspended' ), 100 ); 32 | } 33 | 34 | parent::__construct(); 35 | } 36 | 37 | /** 38 | * Get WPML selected currency. 39 | * 40 | * @return string 41 | */ 42 | public static function get_active_currency() { 43 | if ( ! WC()->session ) { 44 | return get_woocommerce_currency(); 45 | } 46 | 47 | $curr = WC()->session->get( 'wcml_client_currency' ); 48 | if ( empty( $curr ) ) { 49 | $curr = WC()->session->get( 'client_currency' ); 50 | } 51 | 52 | if ( empty( $curr ) ) { 53 | return get_woocommerce_currency(); 54 | } 55 | 56 | return $curr; 57 | } 58 | 59 | /** 60 | * LEGACY v1 METHODS AND HOOKS 61 | */ 62 | 63 | /** 64 | * On OrderReferenceStatus === Suspended, hide currency switcher. 65 | */ 66 | public function remove_currency_switcher_on_order_reference_suspended() { 67 | if ( $this->is_order_reference_checkout_suspended() ) { 68 | // By Pass Multi-currency, so we don't trigger a new set_order_reference_details on process_payment. 69 | $this->bypass_currency_session(); 70 | 71 | // Remove all WPML hooks to display switchers. 72 | remove_action( 'currency_switcher', array( $this->wpml->multi_currency->currency_switcher, 'currency_switcher' ) ); 73 | remove_action( 'woocommerce_product_meta_start', array( $this->wpml->multi_currency->currency_switcher, 'show_currency_switcher' ) ); 74 | remove_action( 'wcml_currency_switcher', array( $this->wpml->multi_currency->currency_switcher, 'wcml_currency_switcher' ) ); 75 | remove_shortcode( 'currency_switcher' ); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-multi-currency.php: -------------------------------------------------------------------------------- 1 | 'WOOCS – Currency Switcher for WooCommerce', 25 | 'class_WC_Product_Price_Based_Country' => 'Price Based on Country for WooCommerce', 26 | 'global_woocommerce_wpml' => 'WPML WooCommerce Multilingual', 27 | 'class_WC_Currency_Converter' => 'Currency Converter Widget', 28 | ); 29 | 30 | /** 31 | * List of compatible regions supportng multi-currency. 32 | */ 33 | const COMPATIBLE_REGIONS = array( 34 | 'eu', 35 | 'gb', 36 | ); 37 | 38 | /** 39 | * WC_Amazon_Payments_Advanced_Multi_Currency constructor. 40 | * 41 | * @param string|null $region Region to inject. 42 | */ 43 | public static function init( $region = null ) { 44 | if ( self::$compatible_instance ) { 45 | return; // already initialized. 46 | } 47 | 48 | /* Filter out the active currency selected. */ 49 | add_filter( 'woocommerce_amazon_pa_active_currency', array( self::class, 'get_active_currency' ) ); 50 | 51 | // Load multicurrency fields if compatibility. (Only on settings admin). 52 | if ( is_admin() ) { 53 | // phpcs:ignore WordPress.Security.NonceVerification 54 | $compatible_region = isset( $_POST['woocommerce_amazon_payments_advanced_payment_region'] ) ? self::compatible_region( sanitize_text_field( $_POST['woocommerce_amazon_payments_advanced_payment_region'] ) ) : self::compatible_region(); 55 | if ( $compatible_region ) { 56 | add_filter( 'woocommerce_amazon_pa_form_fields_before_legacy', array( __CLASS__, 'add_currency_fields' ) ); 57 | } 58 | } 59 | 60 | $region = ! is_null( $region ) ? $region : WC_Amazon_Payments_Advanced_API::get_region(); 61 | 62 | if ( ! self::compatible_region( $region ) ) { 63 | return; 64 | } 65 | 66 | self::init_compatible_plugin_instance(); 67 | } 68 | 69 | /** 70 | * Checks if region is compatible. 71 | * 72 | * @param string|null $region Region to check for compatibility. 73 | * 74 | * @return bool 75 | */ 76 | public static function compatible_region( $region = null ) { 77 | $region = ! is_null( $region ) ? $region : WC_Amazon_Payments_Advanced_API::get_region(); 78 | return is_int( array_search( $region, self::COMPATIBLE_REGIONS, true ) ); 79 | } 80 | 81 | /** 82 | * Singleton to get if there is a compatible instance running. Region can be injected. 83 | * 84 | * @param bool $region Region to check for compatibility. 85 | * 86 | * @return WC_Amazon_Payments_Advanced_Multi_Currency_Abstract 87 | */ 88 | public static function get_compatible_instance( $region = null ) { 89 | if ( ! self::$compatible_instance ) { 90 | self::init(); 91 | } 92 | return self::$compatible_instance; 93 | } 94 | 95 | /** 96 | * Multi-currency is active behind the doors, once there is a compatible plugin (active instance). 97 | * If plugin is frontend compatible, we consider multi-currency not active, since we don't have to intercede. 98 | * 99 | * @return bool 100 | */ 101 | public static function is_active() { 102 | return ( isset( self::$compatible_instance ) && ( ! self::$compatible_instance->is_front_end_compatible() ) ); 103 | } 104 | 105 | /** 106 | * Reload wallet widget wrapper around the instance 107 | * 108 | * @return bool 109 | */ 110 | public static function reload_wallet_widget() { 111 | return self::$compatible_instance->reload_wallet_widget(); 112 | } 113 | 114 | /** 115 | * Get selected currency from selected currency. 116 | * 117 | * @return string 118 | */ 119 | public static function get_selected_currency() { 120 | return self::$compatible_instance::get_active_currency(); 121 | } 122 | 123 | /** 124 | * Currency switched in checkout page. 125 | * If original currency on checkout is different of the current one. 126 | * 127 | * @return bool 128 | */ 129 | public static function is_currency_switched_on_checkout() { 130 | if ( self::$compatible_instance->get_currency_switched_times() > 0 ) { 131 | return true; 132 | } 133 | return false; 134 | } 135 | 136 | /** 137 | * Returns name or global/class name of compatible plugin or false. 138 | * 139 | * @param bool $return_name If name is true, returns commercial name. 140 | * 141 | * @return string 142 | */ 143 | public static function compatible_plugin( $return_name = false ) { 144 | 145 | /** 146 | * Filter out the compatible plugins to allow external sources to add compatibility. 147 | * 148 | * @since 2.5.1 149 | */ 150 | foreach ( apply_filters( 'woocommerce_amazon_pa_ml_compat_plugins', self::COMPATIBLE_PLUGINS ) as $definition_name => $name ) { 151 | $match = false; 152 | if ( 0 === strpos( $definition_name, 'global' ) ) { 153 | $global_name = str_replace( 'global_', '', $definition_name ); 154 | 155 | if ( isset( $GLOBALS[ $global_name ] ) && $GLOBALS[ $global_name ] ) { 156 | $match = true; 157 | } 158 | } elseif ( 0 === strpos( $definition_name, 'class' ) ) { 159 | $class_name = str_replace( 'class_', '', $definition_name ); 160 | if ( class_exists( $class_name ) ) { 161 | $match = true; 162 | } 163 | } 164 | 165 | /** 166 | * Filter out whether the compatible plugin has been located in order to allow external plugins to apply their own logic. 167 | * 168 | * @since 2.5.1 169 | */ 170 | if ( apply_filters( 'woocommerce_amazon_pa_matched_compat_plugin_' . $definition_name, $match ) ) { 171 | return ( $return_name ) ? $name : $definition_name; 172 | } 173 | } 174 | return false; 175 | } 176 | 177 | /** 178 | * Are we on the settings page? 179 | * 180 | * @return bool 181 | */ 182 | public function is_amazon_settings_page() { 183 | if ( is_admin() && 184 | // phpcs:disable WordPress.Security.NonceVerification 185 | ( isset( $_GET['page'] ) && 'wc-settings' === $_GET['page'] ) && 186 | ( isset( $_GET['section'] ) && 'amazon_payments_advanced' === $_GET['section'] ) ) { 187 | // phpcs:enable WordPress.Security.NonceVerification 188 | return true; 189 | } 190 | return false; 191 | } 192 | 193 | /** 194 | * Adds multicurrency settings to form fields. 195 | * 196 | * @param array $form_fields Admin fields. 197 | * @return array 198 | */ 199 | public static function add_currency_fields( $form_fields ) { 200 | if ( ! self::$compatible_instance ) { 201 | return $form_fields; 202 | } 203 | 204 | $compatible_plugin = self::compatible_plugin( true ); 205 | 206 | $form_fields['multicurrency_options'] = array( 207 | 'title' => __( 'Multi-Currency', 'woocommerce-gateway-amazon-payments-advanced' ), 208 | 'type' => 'title', 209 | /* translators: Compatible plugin */ 210 | 'description' => sprintf( __( 'Multi-currency compatibility detected with %s', 'woocommerce-gateway-amazon-payments-advanced' ), $compatible_plugin ), 211 | ); 212 | 213 | /** 214 | * Only show currency list for plugins that will use the list. Frontend plugins will be exempt. 215 | */ 216 | if ( ! self::$compatible_instance->is_front_end_compatible() ) { 217 | $form_fields['currencies_supported'] = array( 218 | 'title' => __( 'Select currencies to display Amazon in your shop', 'woocommerce-gateway-amazon-payments-advanced' ), 219 | 'type' => 'multiselect', 220 | 'options' => WC_Amazon_Payments_Advanced_API::get_supported_currencies( true ), 221 | 'css' => 'height: auto;', 222 | 'custom_attributes' => array( 223 | 'size' => 10, 224 | 'name' => 'currencies_supported', 225 | ), 226 | ); 227 | } 228 | 229 | return $form_fields; 230 | } 231 | 232 | /** 233 | * Searches for the compatible plugin instance. 234 | * 235 | * @param boolean $init When true it will init the found compatible instance and assign it to self::$compatible_instance. 236 | * When false it will just return the full class name of the compatible instance. 237 | * @return string|WC_Amazon_Payments_Advanced_Multi_Currency_Abstract 238 | */ 239 | protected static function init_compatible_plugin_instance( $init = true ) { 240 | $found_plugin_instance = null; 241 | 242 | $compatible_plugin = self::compatible_plugin(); 243 | if ( $compatible_plugin ) { 244 | require_once 'class-wc-amazon-payments-advanced-multi-currency-abstract.php'; 245 | 246 | switch ( $compatible_plugin ) { 247 | case 'global_WOOCS': 248 | require_once 'class-wc-amazon-payments-advanced-multi-currency-woocs.php'; 249 | $found_plugin_instance = $init ? new WC_Amazon_Payments_Advanced_Multi_Currency_Woocs() : WC_Amazon_Payments_Advanced_Multi_Currency_Woocs::class; 250 | break; 251 | case 'class_WC_Product_Price_Based_Country': 252 | require_once 'class-wc-amazon-payments-advanced-multi-currency-ppbc.php'; 253 | $found_plugin_instance = $init ? new WC_Amazon_Payments_Advanced_Multi_Currency_PPBC() : WC_Amazon_Payments_Advanced_Multi_Currency_PPBC::class; 254 | break; 255 | case 'global_woocommerce_wpml': 256 | $wpml_settings = get_option( '_wcml_settings' ); 257 | if ( ( WCML_MULTI_CURRENCIES_DISABLED !== $wpml_settings['enable_multi_currency'] ) ) { 258 | require_once 'class-wc-amazon-payments-advanced-multi-currency-wpml.php'; 259 | $found_plugin_instance = $init ? new WC_Amazon_Payments_Advanced_Multi_Currency_WPML() : WC_Amazon_Payments_Advanced_Multi_Currency_WPML::class; 260 | } 261 | break; 262 | case 'class_WC_Currency_Converter': 263 | require_once 'class-wc-amazon-payments-advanced-multi-currency-wccw.php'; 264 | $found_plugin_instance = $init ? new WC_Amazon_Payments_Advanced_Multi_Currency_WCCW() : WC_Amazon_Payments_Advanced_Multi_Currency_WCCW::class; 265 | break; 266 | default: 267 | /** 268 | * Allow external plugins to return their own compatible instance. 269 | * 270 | * @since 2.5.1 271 | */ 272 | $found_plugin_instance = apply_filters( 'woocommerce_amazon_pa_compat_plugin_instance_' . $compatible_plugin, $found_plugin_instance, $init ); 273 | break; 274 | } 275 | if ( $init ) { 276 | self::$compatible_instance = $found_plugin_instance; 277 | } 278 | return $found_plugin_instance; 279 | } 280 | } 281 | 282 | /** 283 | * Determines current active currency based on the region and the supported multicurrency plugins. 284 | * 285 | * In the case that its not a supported multicurrency region but there is a supported multicurrency plugin 286 | * enabled. We need to find out what's the selected currency is, because Amazon API does not support other currencies 287 | * than JPY in Japan region and USD in United States region. 288 | * 289 | * @param string $currency WooCommerce's currency. 290 | * @return string 291 | */ 292 | public static function get_active_currency( $currency ) { 293 | if ( self::is_active() || self::compatible_region() ) { 294 | return $currency; 295 | } 296 | 297 | $found_plugin_instance = self::init_compatible_plugin_instance( false ); 298 | 299 | return ! is_null( $found_plugin_instance ) ? $found_plugin_instance::get_active_currency() : $currency; 300 | } 301 | } 302 | 303 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-subscribe-to-newsletter-compat.php: -------------------------------------------------------------------------------- 1 | plugin_url . '/build/js/non-block/amazon-wc-subscribe-to-newsletter-compat' . $js_suffix; 34 | wp_enqueue_script( 'amazon_pa_subscribe_to_newsletter_compat', $url, array( 'amazon_payments_advanced' ), wc_apa()->version, true ); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-wgm-compat.php: -------------------------------------------------------------------------------- 1 | wgm_api_exists() ) { 32 | return; 33 | } 34 | 35 | if ( $this->is_confirm_and_place_order_page() && $this->has_amazon_reference_id() ) { 36 | add_action( 'woocommerce_checkout_init', array( $this, 'remove_ui_hooks' ), 99 ); 37 | add_filter( 'woocommerce_pa_hijack_checkout_fields', '__return_false' ); 38 | 39 | $this->store_address_details(); 40 | } 41 | } 42 | 43 | /** 44 | * Check if WGM API exists. 45 | * 46 | * @since 1.6.0 47 | * 48 | * @return bool Returns true if WGM API exists 49 | */ 50 | public function wgm_api_exists() { 51 | if ( ! class_exists( 'WGM_Session' ) ) { 52 | return false; 53 | } 54 | if ( ! is_callable( array( 'WGM_Session', 'is_set' ) ) ) { 55 | return false; 56 | } 57 | 58 | if ( ! class_exists( 'WGM_Helper' ) ) { 59 | return false; 60 | } 61 | if ( ! is_callable( array( 'WGM_Helper', 'get_wgm_option' ) ) ) { 62 | return false; 63 | } 64 | 65 | return true; 66 | } 67 | 68 | /** 69 | * Check if Amazon reference ID exists in WGM session. 70 | * 71 | * @since 1.6.0 72 | * 73 | * @return bool Returns true if WGM Session has Amazon reference ID 74 | */ 75 | public function has_amazon_reference_id() { 76 | return WGM_Session::is_set( 'amazon_reference_id', 'first_checkout_post_array' ); 77 | } 78 | 79 | /** 80 | * Retrieve Amazon reference ID from WGM Session. 81 | * 82 | * @since 1.6.0 83 | * 84 | * @return string Amazon reference ID 85 | */ 86 | public function get_amazon_reference_id() { 87 | return WGM_Session::get( 'amazon_reference_id', 'first_checkout_post_array' ); 88 | } 89 | 90 | /** 91 | * Retrieve Amazon access token from WGM Session. 92 | * 93 | * @since 1.6.0 94 | * 95 | * @return string Amazon access token 96 | */ 97 | public function get_amazon_access_token() { 98 | return WGM_Session::get( 'amazon_access_token', 'first_checkout_post_array' ); 99 | } 100 | 101 | /** 102 | * Check if current page is confirm and place order page from WGM. 103 | * 104 | * @since 1.6.0 105 | * 106 | * @return bool Return true if current page is confirm and place order page 107 | * from WGM. 108 | */ 109 | public function is_confirm_and_place_order_page() { 110 | return ( 111 | is_page( WGM_Helper::get_wgm_option( 'check' ) ) 112 | || 113 | wc_post_content_has_shortcode( 'woocommerce_de_check' ) 114 | ); 115 | } 116 | 117 | /** 118 | * Remove any attempt to initialize Amazon widget on second checkout page. 119 | * 120 | * @since 1.6.0 121 | */ 122 | public function remove_ui_hooks() { 123 | remove_action( 'woocommerce_checkout_before_customer_details', array( wc_apa(), 'payment_widget' ), 20 ); 124 | remove_action( 'woocommerce_checkout_before_customer_details', array( wc_apa(), 'address_widget' ), 10 ); 125 | } 126 | 127 | /** 128 | * Store address details from Amazon in WGM Session. 129 | * 130 | * @since 1.6.0 131 | */ 132 | public function store_address_details() { 133 | $order_reference_details = $this->get_amazon_order_details(); 134 | if ( ! $order_reference_details ) { 135 | return; 136 | } 137 | 138 | // @codingStandardsIgnoreStart 139 | $buyer = $order_reference_details->Buyer; 140 | $destination = $order_reference_details->Destination->PhysicalDestination; 141 | $shipping_info = WC_Amazon_Payments_Advanced_API::format_address( $destination ); 142 | 143 | $this->set_address_in_wgm( $shipping_info, 'shipping' ); 144 | 145 | // Some market API endpoint return billing address information, parse it if present. 146 | if ( isset( $order_reference_details->BillingAddress->PhysicalAddress ) ) { 147 | 148 | $billing_address = WC_Amazon_Payments_Advanced_API::format_address( $order_reference_details->BillingAddress->PhysicalAddress ); 149 | 150 | } elseif ( apply_filters( 'woocommerce_amazon_pa_billing_address_fallback_to_shipping_address', true ) ) { 151 | 152 | // Reuse the shipping address information if no bespoke billing info. 153 | $billing_address = $shipping_info; 154 | 155 | } else { 156 | $name = ! empty( $buyer->Name ) ? (string) $buyer->Name : ''; 157 | $billing_address = WC_Amazon_Payments_Advanced_API::format_name( $name ); 158 | } 159 | 160 | $billing_address['email'] = (string) $buyer->Email; 161 | $billing_address['phone'] = isset( $billing_address['phone'] ) ? $billing_address['phone'] : (string) $buyer->Phone; 162 | // @codingStandardsIgnoreEnd 163 | 164 | $this->set_address_in_wgm( $billing_address, 'billing' ); 165 | } 166 | 167 | /** 168 | * Set address details of given type in WGM Session. 169 | * 170 | * @since 1.6.0 171 | * 172 | * @param array $address Address details. 173 | * @param string $type Address type ('billing' or 'shipping'). 174 | */ 175 | public function set_address_in_wgm( $address, $type = 'billing' ) { 176 | $checkout = WC_Checkout::instance(); 177 | foreach ( $address as $key => $value ) { 178 | $key = $type . '_' . $key; 179 | WGM_Session::add( $key, $value, 'first_checkout_post_array' ); 180 | } 181 | } 182 | 183 | /** 184 | * Get amazon order details. 185 | * 186 | * @since 1.6.0 187 | * 188 | * @return bool|Object Returns OrderReferenceDetails object if succeed, 189 | * otherwise false is returned 190 | */ 191 | public function get_amazon_order_details() { 192 | $request_args = array( 193 | 'Action' => 'GetOrderReferenceDetails', 194 | 'AmazonOrderReferenceId' => $this->get_amazon_reference_id(), 195 | ); 196 | 197 | /** 198 | * Full address information is available to the 'GetOrderReferenceDetails' 199 | * call when we're in "login app" mode and we pass the AddressConsentToken 200 | * to the API. 201 | * 202 | * @see "Getting the Shipping Address" section here: https://payments.amazon.com/documentation/lpwa/201749990 203 | */ 204 | $settings = WC_Amazon_Payments_Advanced_API::get_settings(); 205 | 206 | if ( 'yes' === $settings['enable_login_app'] ) { 207 | $request_args['AddressConsentToken'] = $this->get_amazon_access_token(); 208 | } 209 | 210 | $response = WC_Amazon_Payments_Advanced_API_Legacy::request( $request_args ); 211 | // @codingStandardsIgnoreStart 212 | if ( ! is_wp_error( $response ) && isset( $response->GetOrderReferenceDetailsResult->OrderReferenceDetails ) ) { 213 | return $response->GetOrderReferenceDetailsResult->OrderReferenceDetails; 214 | } 215 | // @codingStandardsIgnoreEnd 216 | 217 | return false; 218 | } 219 | } 220 | 221 | -------------------------------------------------------------------------------- /includes/compats/class-wc-amazon-payments-advanced-woocommerce-multilingual-compat.php: -------------------------------------------------------------------------------- 1 | needs_payment() to become `false`. 37 | add_filter( 'woocommerce_cart_needs_payment', '__return_true' ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /includes/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compat-abstract.php: -------------------------------------------------------------------------------- 1 | name ) || ! isset( $this->settings_name ) ) { 24 | throw new Exception( 'You have to set both the properties name and settings_name when extending the class ' . __CLASS__ . ' !' ); 25 | } 26 | } 27 | 28 | /** 29 | * Gets called during the server side initialization and sets our settings. 30 | * 31 | * Overwrite when you need different set of logic. 32 | * 33 | * @return void 34 | */ 35 | public function initialize() { 36 | $this->settings = get_option( $this->settings_name, array() ); 37 | } 38 | 39 | /** 40 | * Returns if the Payment Method is active. 41 | * 42 | * Overwrite when you need different set of logic. 43 | * 44 | * @return boolean 45 | */ 46 | public function is_active() { 47 | return ! empty( $this->settings['enabled'] ) && 'yes' === $this->settings['enabled']; 48 | } 49 | 50 | /** 51 | * Returns the frontend scripts required by the payment method. 52 | * 53 | * Return an array of script handles that have been registered already. 54 | * 55 | * @return array 56 | */ 57 | public function get_payment_method_script_handles() { 58 | return $this->scripts_name_per_type( 'frontend' ); 59 | } 60 | 61 | /** 62 | * Returns the backend scripts required by the payment method. 63 | * 64 | * Return an array of script handles that have been registered already. 65 | * 66 | * @return array 67 | */ 68 | public function get_payment_method_script_handles_for_admin() { 69 | return $this->scripts_name_per_type( 'backend' ); 70 | } 71 | 72 | /** 73 | * Returns the frontend accessible data. 74 | * 75 | * Can be accessed by calling 76 | * const settings = wc.wcSettings.getSetting( '{paymentMethodName}_data' ); 77 | * 78 | * @return array 79 | */ 80 | public function get_payment_method_data() { 81 | return array( 82 | 'title' => $this->settings['title'], 83 | 'description' => $this->settings['description'], 84 | 'supports' => $this->get_supported_features(), 85 | ); 86 | } 87 | 88 | /** 89 | * Returns the scripts required by the payment method based on the $type param. 90 | * 91 | * @param string $type Can be 'backend' or 'frontend'. 92 | * @return array Return an array of script handles that have been registered already. 93 | */ 94 | abstract protected function scripts_name_per_type( $type = '' ); 95 | 96 | /** 97 | * Returns an array of supported features. 98 | * 99 | * @return string[] 100 | */ 101 | public function get_supported_features() { 102 | $gateways = WC()->payment_gateways->get_available_payment_gateways(); 103 | if ( isset( $gateways[ $this->name ] ) ) { 104 | return $gateways[ $this->name ]->supports; 105 | } 106 | return array(); 107 | } 108 | 109 | /** 110 | * Returns supported currencies if multi currency is enabled. 111 | * 112 | * @return array|false 113 | */ 114 | protected function get_allowed_currencies() { 115 | if ( ! WC_Amazon_Payments_Advanced_Multi_Currency::is_active() ) { 116 | return false; 117 | } 118 | 119 | return array_values( WC_Amazon_Payments_Advanced_API::get_selected_currencies() ); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /includes/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compat-classic.php: -------------------------------------------------------------------------------- 1 | get_gateway(); 34 | return $wc_apa_gateway->is_available() && $wc_apa_gateway->is_classic_enabled() && ! $wc_apa_gateway->get_checkout_session_id(); 35 | } 36 | 37 | /** 38 | * Returns the frontend accessible data. 39 | * 40 | * Can be accessed by calling 41 | * const settings = wc.wcSettings.getSetting( '{paymentMethodName}_data' ); 42 | * 43 | * @return array 44 | */ 45 | public function get_payment_method_data() { 46 | return array( 47 | 'title' => $this->settings['title'], 48 | 'description' => $this->settings['description'], 49 | 'supports' => $this->get_supported_features(), 50 | 'amazonPayPreviewUrl' => esc_url( wc_apa()->plugin_url . '/build/images/amazon-pay-preview.png' ), 51 | 'action' => wc_apa()->get_gateway()->get_current_cart_action(), 52 | 'allowedCurrencies' => $this->get_allowed_currencies(), 53 | ); 54 | } 55 | 56 | /** 57 | * Returns the scripts required by the payment method based on the $type param. 58 | * 59 | * @param string $type Can be 'backend' or 'frontend'. 60 | * @return array Return an array of script handles that have been registered already. 61 | */ 62 | protected function scripts_name_per_type( $type = '' ) { 63 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 64 | 65 | $script_data = include wc_apa()->path . '/build/js/payments-methods/classic/index.asset.php'; 66 | wp_register_script( 'amazon_payments_advanced_classic_block_compat', wc_apa()->plugin_url . '/build/js/payments-methods/classic/index' . $min . '.js', $script_data['dependencies'], $script_data['version'], true ); 67 | wp_set_script_translations( 'amazon_payments_advanced_classic_block_compat', 'woocommerce-gateway-amazon-payments-advanced' ); 68 | 69 | return array( 'amazon_payments_advanced_classic_block_compat' ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /includes/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compat-express.php: -------------------------------------------------------------------------------- 1 | should_express_be_loaded() && wc_apa()->get_gateway()->is_available(); 34 | } 35 | 36 | /** 37 | * Returns the frontend accessible data. 38 | * 39 | * Can be accessed by calling 40 | * const settings = wc.wcSettings.getSetting( '{paymentMethodName}_data' ); 41 | * 42 | * @return array 43 | */ 44 | public function get_payment_method_data() { 45 | $wc_apa_gateway = wc_apa()->get_gateway(); 46 | 47 | $checkout_session = $wc_apa_gateway->get_checkout_session_id() ? $wc_apa_gateway->get_checkout_session() : null; 48 | 49 | return array( 50 | 'title' => $this->settings['title'], 51 | 'description' => $this->settings['description'], 52 | 'hide_button_mode' => $this->settings['hide_button_mode'], 53 | 'loggedIn' => ! is_admin() && $checkout_session && ! is_wp_error( $checkout_session ) && true === $wc_apa_gateway->is_checkout_session_still_valid( $checkout_session ), 54 | 'supports' => $this->get_supported_features(), 55 | 'logoutUrl' => $wc_apa_gateway->get_amazon_logout_url(), 56 | 'logoutMessage' => apply_filters( 'woocommerce_amazon_pa_checkout_logout_message', __( 'You\'re logged in with your Amazon Account.', 'woocommerce-gateway-amazon-payments-advanced' ) ), 57 | 'selectedPaymentMethod' => esc_html( $wc_apa_gateway->get_selected_payment_label( $checkout_session, empty( $checkout_session ) ) ), 58 | 'hasPaymentPreferences' => $wc_apa_gateway->has_payment_preferences( $checkout_session, empty( $checkout_session ) ), 59 | 'hasPaymentPreferences' => false, 60 | 'allOtherGateways' => $this->gateways_to_unset_on_fe(), 61 | 'allowedCurrencies' => $this->get_allowed_currencies(), 62 | 'amazonPayPreviewUrl' => esc_url( wc_apa()->plugin_url . '/build/images/amazon-pay-preview.png' ), 63 | 'amazonAddress' => array( 64 | 'amazonBilling' => $checkout_session && ! is_wp_error( $checkout_session ) && ! empty( $checkout_session->billingAddress ) ? WC_Amazon_Payments_Advanced_API::format_address( $checkout_session->billingAddress ) : null, // phpcs:ignore WordPress.NamingConventions 65 | 'amazonShipping' => $checkout_session && ! is_wp_error( $checkout_session ) && ! empty( $checkout_session->shippingAddress ) ? WC_Amazon_Payments_Advanced_API::format_address( $checkout_session->shippingAddress ) : null, // phpcs:ignore WordPress.NamingConventions 66 | ), 67 | ); 68 | 69 | } 70 | 71 | /** 72 | * Returns the scripts required by the payment method based on the $type param. 73 | * 74 | * @param string $type Can be 'backend' or 'frontend'. 75 | * @return array Return an array of script handles that have been registered already. 76 | */ 77 | protected function scripts_name_per_type( $type = '' ) { 78 | $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min'; 79 | 80 | /* Registering Express Payment Script. */ 81 | $script_data = include wc_apa()->path . '/build/js/payments-methods/express/index.asset.php'; 82 | wp_register_script( 'amazon_payments_advanced_express_block_compat', wc_apa()->plugin_url . '/build/js/payments-methods/express/index' . $min . '.js', $script_data['dependencies'], $script_data['version'], true ); 83 | wp_set_script_translations( 'amazon_payments_advanced_express_block_compat', 'woocommerce-gateway-amazon-payments-advanced' ); 84 | 85 | return array( 'amazon_payments_advanced_express_block_compat' ); 86 | } 87 | 88 | /** 89 | * Returns an array of supported features. 90 | * 91 | * @return string[] 92 | */ 93 | public function get_supported_features() { 94 | $gateways = WC()->payment_gateways->get_available_payment_gateways(); 95 | if ( isset( $gateways['amazon_payments_advanced'] ) ) { 96 | return $gateways['amazon_payments_advanced']->supports; 97 | } 98 | return array(); 99 | } 100 | 101 | /** 102 | * Returns an array of payment method ids to unset on the frontend. 103 | * 104 | * @return array 105 | */ 106 | protected function gateways_to_unset_on_fe() { 107 | $available_gateways = WC()->payment_gateways->payment_gateways(); 108 | 109 | $express_gateway = wc_apa()->get_express_gateway(); 110 | 111 | if ( null === $express_gateway ) { 112 | return array(); 113 | } 114 | 115 | $regular_gateway = wc_apa()->get_gateway(); 116 | 117 | if ( empty( $express_gateway->id ) || empty( $regular_gateway->id ) ) { 118 | return array(); 119 | } 120 | 121 | if ( empty( $available_gateways[ $express_gateway->id ] ) || empty( $available_gateways[ $regular_gateway->id ] ) ) { 122 | return array(); 123 | } 124 | 125 | return array_diff( array_keys( $available_gateways ), array( $express_gateway->id, $regular_gateway->id ) ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /includes/compats/woo-blocks/class-wc-amazon-payments-advanced-block-compatibility.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/class-wc-amazon-payments-advanced-block-compat-classic.php', 22 | 'WC_Amazon_Payments_Advanced_Block_Compat_Express' => __DIR__ . '/class-wc-amazon-payments-advanced-block-compat-express.php', 23 | ); 24 | 25 | /** 26 | * Registers the compatible classes to the PaymentMethodRegistry. 27 | * 28 | * Hooked on woocommerce_blocks_payment_method_type_registration 29 | * 30 | * @param PaymentMethodRegistry $registry WooCommerce Block's registry instance. 31 | * @return void 32 | */ 33 | public static function init( PaymentMethodRegistry $registry ) { 34 | $compats = apply_filters( 'woocommerce_amazon_pa_block_compatibility_class_array', self::$file_class_compats_map ); 35 | if ( ! empty( $compats ) ) { 36 | require_once __DIR__ . '/class-wc-amazon-payments-advanced-block-compat-abstract.php'; 37 | } 38 | foreach ( $compats as $class => $path ) { 39 | if ( file_exists( $path ) ) { 40 | require_once $path; 41 | if ( class_exists( $class ) ) { 42 | $registry->register( new $class() ); 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /includes/legacy/class-wc-amazon-payments-advanced-ipn-handler-legacy.php: -------------------------------------------------------------------------------- 1 | validate_required_keys( $message['Message'], $this->required_notification_message_keys_v1 ); 72 | } 73 | 74 | /** 75 | * Validate IPN Legacy Message for subscriptions 76 | * 77 | * @param mixed $message IPN Message. 78 | */ 79 | public function validate_subscription_keys_v1( $message ) { 80 | $this->validate_required_keys( $message['Message'], $this->required_subscription_keys_v1 ); 81 | } 82 | 83 | /** 84 | * Handle the IPN. 85 | * 86 | * At this point, notification message is validated already. 87 | * 88 | * @throws Exception Missing handler for the notification type. 89 | * 90 | * @since 1.8.0 91 | * @version 1.8.0 92 | * 93 | * @param array $message Parsed SNS message. 94 | */ 95 | public function handle_notification_ipn_v1( $message ) { 96 | // Ignore non-notification type message. 97 | if ( 'Notification' !== $message['Type'] ) { 98 | return; 99 | } 100 | 101 | $notification_version = isset( $message['Message']['NotificationVersion'] ) ? strtolower( $message['Message']['NotificationVersion'] ) : 'v1'; 102 | 103 | if ( 'v1' !== $notification_version || WC_Amazon_Payments_Advanced_Merchant_Onboarding_Handler::get_migration_status() ) { 104 | return; 105 | } 106 | 107 | $notification = $message['Message']; 108 | $notification_data = $this->get_parsed_notification_data( $notification ); 109 | $order = $this->get_order_from_notification_data( $notification['NotificationType'], $notification_data ); 110 | 111 | do_action( 'woocommerce_amazon_payments_advanced_handle_ipn_order', $order ); 112 | 113 | switch ( $notification['NotificationType'] ) { 114 | case 'OrderReferenceNotification': 115 | $this->handle_ipn_order_reference( $order, $notification_data ); 116 | break; 117 | case 'PaymentAuthorize': 118 | $this->handle_ipn_payment_authorize( $order, $notification_data ); 119 | break; 120 | case 'PaymentCapture': 121 | $this->handle_ipn_payment_capture( $order, $notification_data ); 122 | break; 123 | case 'PaymentRefund': 124 | $this->handle_ipn_payment_refund( $order, $notification_data ); 125 | break; 126 | default: 127 | throw new Exception( 'No handler for notification with type ' . $notification['NotificationType'] ); 128 | } 129 | } 130 | 131 | /** 132 | * Handle IPN for order reference notification. 133 | * 134 | * Currently only log the event in the order notes. 135 | * 136 | * @since 1.8.0 137 | * @version 1.8.0 138 | * 139 | * @param WC_Order $order Order object. 140 | * @param SimpleXMLElement $notification_data Notification data. 141 | */ 142 | protected function handle_ipn_order_reference( $order, $notification_data ) { 143 | // @codingStandardsIgnoreStart 144 | $order->add_order_note( sprintf( 145 | /* translators: 1) Amazon order reference ID 2) order reference status */ 146 | __( 'Received IPN for order reference %1$s with status %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), 147 | (string) $notification_data->OrderReference->AmazonOrderReferenceId, 148 | (string) $notification_data->OrderReference->OrderReferenceStatus->State 149 | ) ); 150 | // @codingStandardsIgnoreEnd 151 | 152 | if ( $order->get_meta( 'amazon_timed_out_transaction' ) ) { 153 | WC_Amazon_Payments_Advanced_API_Legacy::handle_async_ipn_order_reference_payload( $notification_data, $order ); 154 | } 155 | } 156 | 157 | /** 158 | * Handle IPN for payment authorize notification. 159 | * 160 | * Currently only log the event in the order notes. 161 | * 162 | * @since 1.8.0 163 | * @version 1.8.0 164 | * 165 | * @param WC_Order $order Order object. 166 | * @param SimpleXMLElement $notification_data Notification data. 167 | */ 168 | protected function handle_ipn_payment_authorize( $order, $notification_data ) { 169 | // @codingStandardsIgnoreStart 170 | $order->add_order_note( sprintf( 171 | /* translators: 1) Amazon authorize ID 2) authorization status */ 172 | __( 'Received IPN for payment authorize %1$s with status %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), 173 | (string) $notification_data->AuthorizationDetails->AmazonAuthorizationId, 174 | (string) $notification_data->AuthorizationDetails->AuthorizationStatus->State 175 | ) ); 176 | // @codingStandardsIgnoreEnd 177 | 178 | if ( $order->get_meta( 'amazon_timed_out_transaction' ) ) { 179 | WC_Amazon_Payments_Advanced_API_Legacy::handle_async_ipn_payment_authorization_payload( $notification_data, $order ); 180 | } 181 | } 182 | 183 | /** 184 | * Handle IPN for payment capture notification. 185 | * 186 | * Currently only log the event in the order notes. 187 | * 188 | * @since 1.8.0 189 | * @version 1.8.0 190 | * 191 | * @param WC_Order $order Order object. 192 | * @param SimpleXMLElement $notification_data Notification data. 193 | */ 194 | protected function handle_ipn_payment_capture( $order, $notification_data ) { 195 | // @codingStandardsIgnoreStart 196 | $order->add_order_note( sprintf( 197 | /* translators: 1) Amazon capture ID 2) capture status */ 198 | __( 'Received IPN for payment capture %1$s with status %2$s.', 'woocommerce-gateway-amazon-payments-advanced' ), 199 | (string) $notification_data->CaptureDetails->AmazonCaptureId, 200 | (string) $notification_data->CaptureDetails->CaptureStatus->State 201 | ) ); 202 | // @codingStandardsIgnoreEnd 203 | } 204 | 205 | /** 206 | * Handle IPN for payment payment refund. 207 | * 208 | * Currently only log the event in the order notes. 209 | * 210 | * @since 1.8.0 211 | * @version 1.8.0 212 | * 213 | * @param WC_Order $order Order object. 214 | * @param SimpleXMLElement $notification_data Notification data. 215 | */ 216 | protected function handle_ipn_payment_refund( $order, $notification_data ) { 217 | // @codingStandardsIgnoreStart 218 | $refund_id = (string) $notification_data->RefundDetails->AmazonRefundId; 219 | $refund_status = (string) $notification_data->RefundDetails->RefundStatus->State; 220 | $refund_amount = (float) $notification_data->RefundDetails->RefundAmount->Amount; 221 | $refund_type = (string) $notification_data->RefundDetails->RefundType; 222 | $refund_reason = (string) $notification_data->RefundDetails->SellerRefundNote; 223 | // @codingStandardsIgnoreEnd 224 | 225 | $order->add_order_note( 226 | sprintf( 227 | // translators: 1) Amazon refund ID 2) refund status 3) refund amount. 228 | __( 'Received IPN for payment refund %1$s with status %2$s. Refund amount: %3$s.', 'woocommerce-gateway-amazon-payments-advanced' ), 229 | $refund_id, 230 | $refund_status, 231 | wc_price( $refund_amount ) 232 | ) 233 | ); 234 | 235 | if ( 'refunded' === $order->get_status() ) { 236 | return; 237 | } 238 | 239 | $max_refund = wc_format_decimal( $order->get_total() - $order->get_total_refunded() ); 240 | 241 | if ( ! $max_refund ) { 242 | return; 243 | } 244 | 245 | $refund_amount = min( $refund_amount, $max_refund ); 246 | 247 | $order_id = wc_apa_get_order_prop( $order, 'id' ); 248 | 249 | $wc_refund = false; 250 | $previous_refunds = wp_list_pluck( $order->get_meta( 'amazon_refund_id', false ), 'value' ); 251 | if ( ! empty( $previous_refunds ) ) { 252 | foreach ( $previous_refunds as $this_refund_id ) { 253 | if ( $this_refund_id === $refund_id ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase 254 | $wc_refund = true; 255 | break; 256 | } 257 | } 258 | } 259 | if ( empty( $wc_refund ) ) { 260 | wc_create_refund( 261 | array( 262 | 'amount' => $refund_amount, 263 | 'reason' => $refund_reason, 264 | 'order_id' => $order_id, 265 | ) 266 | ); 267 | 268 | $order->update_meta_data( 'amazon_refund_id', $refund_id ); 269 | $order->save(); 270 | } 271 | 272 | // Buyer canceled the order. 273 | if ( 'BuyerCanceled' === $refund_type ) { 274 | $order->update_status( 'cancelled', __( 'Order cancelled by customer via Amazon.', 'woocommerce-gateway-amazon-payments-advanced' ) ); 275 | } 276 | } 277 | 278 | /** 279 | * Get the parsed notification data (from XML). 280 | * 281 | * @since 1.8.0 282 | * 283 | * @throws Exception Failed to parse the XML. 284 | * 285 | * @param array $notification Notification message. 286 | * 287 | * @return SimpleXMLElement Parsed XML object. 288 | */ 289 | protected function get_parsed_notification_data( $notification ) { 290 | // Chargeback notification has different notification message and it's 291 | // not XML, so return as it's. 292 | if ( 'ChargebackDetailedNotification' === $notification['NotificationType'] ) { 293 | return $notification['NotificationData']; 294 | } 295 | 296 | $data = WC_Amazon_Payments_Advanced_API_Legacy::safe_load_xml( $notification['NotificationData'], LIBXML_NOCDATA ); 297 | if ( ! $data ) { 298 | throw new Exception( 'Failed to parse the XML in NotificationData.' ); 299 | } 300 | 301 | return $data; 302 | } 303 | 304 | /** 305 | * Get order from notification data. 306 | * 307 | * @since 1.8.0 308 | * 309 | * @throws Exception Failed to get order information from notification data. 310 | * 311 | * @param string $notification_type Notification type. 312 | * @param SimpleXMLElement $notification_data Notification data. 313 | * 314 | * @return WC_Order Order object. 315 | */ 316 | protected function get_order_from_notification_data( $notification_type, $notification_data ) { 317 | $order_id = null; 318 | 319 | // @codingStandardsIgnoreStart 320 | switch ( $notification_type ) { 321 | case 'OrderReferenceNotification': 322 | $order_id = (int) $notification_data->OrderReference->SellerOrderAttributes->SellerOrderId; 323 | break; 324 | case 'PaymentAuthorize': 325 | $auth_ref = (string) $notification_data->AuthorizationDetails->AuthorizationReferenceId; 326 | $auth_ref = explode( '-', $auth_ref ); 327 | $order_id = $auth_ref[0]; 328 | break; 329 | case 'PaymentCapture': 330 | $capture_ref = (string) $notification_data->CaptureDetails->CaptureReferenceId; 331 | $capture_ref = explode( '-', $capture_ref ); 332 | $order_id = $capture_ref[0]; 333 | break; 334 | case 'PaymentRefund': 335 | $refund_id = (string) $notification_data->RefundDetails->AmazonRefundId; 336 | $refund_parts = explode( '-', $refund_id ); 337 | unset( $refund_parts[3] ); 338 | 339 | $order_ref = implode( '-', $refund_parts ); 340 | $order_id = WC_Amazon_Payments_Advanced_API_Legacy::get_order_id_from_reference_id( $order_ref ); 341 | 342 | // When no order stores refund reference ID, checks RefundReferenceId. 343 | if ( ! $order_id ) { 344 | $refund_ref = (string) $notification_data->RefundDetails->RefundReferenceId; 345 | $refund_ref = explode( '-', $refund_ref ); 346 | $order_id = $refund_ref[0]; 347 | } 348 | break; 349 | } 350 | // @codingStandardsIgnoreEnd 351 | 352 | if ( ! $order_id ) { 353 | throw new Exception( 'Could not found order information from notification data.' ); 354 | } 355 | 356 | $order = wc_get_order( $order_id ); 357 | if ( ! $order ) { 358 | throw new Exception( 'Invalid order ID ' . $order_id ); 359 | } 360 | 361 | return $order; 362 | } 363 | 364 | /** 365 | * Process pending syncronuos payments. 366 | * 367 | * @param int $order_id Order ID. 368 | * @param string $amazon_authorization_id Authorization ID. 369 | * @return null|WP_Error WP_Error on error, null if processed 370 | */ 371 | public function process_pending_syncro_payments( $order_id, $amazon_authorization_id ) { 372 | 373 | wc_apa()->log( sprintf( 'Processing pending synchronous payment. Order: %s, Auth ID: %s', $order_id, $amazon_authorization_id ) ); 374 | $order = wc_get_order( $order_id ); 375 | if ( ! $order ) { 376 | return new WP_Error( 'invalid_order', __( 'Invalid order.', 'woocommerce-gateway-amazon-payments-advanced' ) ); 377 | } 378 | 379 | try { 380 | $response = WC_Amazon_Payments_Advanced_API_Legacy::request( 381 | array( 382 | 'Action' => 'GetAuthorizationDetails', 383 | 'AmazonAuthorizationId' => $amazon_authorization_id, 384 | ) 385 | ); 386 | if ( $order->get_meta( 'amazon_timed_out_transaction' ) ) { 387 | WC_Amazon_Payments_Advanced_API_Legacy::handle_synch_payment_authorization_payload( $response, $order, $amazon_authorization_id ); 388 | } 389 | } catch ( Exception $e ) { 390 | /* translators: placeholder is error message from Amazon Pay API */ 391 | $order->add_order_note( sprintf( __( 'Error: Unable to authorize funds with Amazon. Reason: %s', 'woocommerce-gateway-amazon-payments-advanced' ), $e->getMessage() ) ); 392 | } 393 | } 394 | 395 | /** 396 | * Unschedule Action for Order. 397 | * 398 | * @param WC_Order $order Order object. 399 | */ 400 | public function unschedule_pending_syncro_payments( $order ) { 401 | // Unschedule the Action for this order. 402 | $args = array( 403 | 'order_id' => $order->get_id(), 404 | 'amazon_authorization_id' => $order->get_meta( 'amazon_authorization_id', true ), 405 | ); 406 | as_unschedule_action( 'wcga_process_pending_syncro_payments', $args ); 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 3 3 | treatPhpDocTypesAsCertain: false 4 | scanDirectories: 5 | - ../woocommerce-subscriptions/ 6 | bootstrapFiles: 7 | - %currentWorkingDirectory%/vendor/php-stubs/wordpress-stubs/wordpress-stubs.php 8 | - %currentWorkingDirectory%/vendor/php-stubs/woocommerce-stubs/woocommerce-stubs.php 9 | paths: 10 | - woocommerce-gateway-amazon-payments-advanced.php 11 | - includes/ 12 | excludePaths: 13 | analyse: 14 | - *-legacy.php 15 | - includes/compats/ 16 | ignoreErrors: 17 | # Stub causes errors 18 | - '#Function apply_filters invoked with#' 19 | # PHPStan analysis error 20 | - "#Cannot unset offset 'enable_login_app'#" 21 | - '#Access to an undefined property WC_Subscription::\$handled_cancel#' 22 | - '#Call to an undefined method WC_Order::get_related_orders#' 23 | # WC Stub issues 24 | - '#Access to an undefined property WC_Cart::#' 25 | - '#Access to an undefined property WC_Session::#' 26 | - '#Call to an undefined method WC_Data_Store::get_zones#' 27 | - '#Call to an undefined method WC_Session::save_data#' 28 | - '#Access to an undefined property WC_Checkout::\$checkout_fields#' 29 | 30 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Amazon Pay for WooCommerce === 2 | Contributors: woocommerce, automattic, saucal, woothemes, akeda, jeffstieler, mikejolley, bor0, claudiosanches, royho, jamesrrodger, laurendavissmith001, dwainm, danreylop 3 | Tags: woocommerce, amazon, checkout, payments, e-commerce, ecommerce 4 | Requires at least: 5.5 5 | Tested up to: 6.7 6 | Stable tag: 2.5.4 7 | License: GPLv3 8 | License URI: http://www.gnu.org/licenses/gpl-3.0.html 9 | 10 | Install the Amazon Pay plugin for your WooCommerce store and take advantage of a seamless checkout experience 11 | 12 | == Description == 13 | 14 | **What is Amazon Pay?** An end-to-end payment solution that gives hundreds of millions of active Amazon customers [1] a familiar, fast, and secure way to complete their purchase through your online store. Shoppers can use the address and payment information already stored in their Amazon account to check out – avoiding account creation or the need to re-enter their billing and shipping information. The performance is continually optimized by technology, learnings, and best practices from Amazon. 15 | 16 | As earth’s most customer-centric company, we are continuously innovating on behalf of our customers. With 91% of Amazon Pay customers saying they would use Amazon Pay again and hundreds of millions of active Amazon customers already enabled for Amazon Pay, it can make it easier for you to deliver an improved customer experience online [2]. 17 | 18 | = The benefits of using Amazon Pay = 19 | 20 | **Keep customers engaged – from cart to finish** 21 | 37% percent of customers abandon a site because they’re asked to create an account.[3] With Amazon Pay, there’s no need to create a new account or enter new information on your site. 22 | 23 | **Reduce chargebacks and fraudulent transactions** 24 | The Amazon brand is a proven winner of customer trust.[4] Our advanced fraud protection is the same technology used on Amazon.com 25 | 26 | **Grow your audience with co-marketing initiatives** 27 | The cost of acquiring new customers has increased by over 50% over the last five years.[5] With Amazon Pay co-marketing programs, tap into Amazon’s customer base of 200m+ global Prime customers. 28 | 29 | **Leverage the latest technology that customers love** 30 | Benefit from Amazon’s ecommerce innovations, enable features like recurring payments, let customers shop with their voice using Alexa, and much more. 31 | 32 | = Key Features = 33 | 34 | - **PSD2 compliant**: Built-in support for Strong Customer Authentication (SCA) as required under the Second Payment Services Directive (PSD2) in the European Economic Area (EEA). 35 | - **Multi-currency**: Maintain the local currency experience across the shopping journey and help customers avoid currency conversion fees from their credit card issuer or bank. 36 | - **Recurring payment support for [WooCommerce Subscriptions](https://woocommerce.com/products/woocommerce-subscriptions/)** (separate purchase): available for USA, UK, Germany, France, Italy, Ireland, Spain, Luxembourg, Austria, Belgium, Cyprus, Netherlands, Sweden, Portugal, Hungary, Denmark and Japan. 37 | - **Automatic Decline Handling**: Reduce lost sales with a consistent experience for customers to gracefully recover from a declined payment. 38 | - **Payment Protection Policy**: Protection against fraud-related chargebacks[6]. 39 | - **Amazon Pay A-to-z Guarantee**: Increase customer confidence to complete purchase in your online store with extra assurance on the timeliness of delivery and order quality[7] . 40 | - **Delivery Notifications**: Proactively alert customers on the arrival status of physical goods orders via Amazon Alexa[8]. 41 | 42 | = Definitions = 43 | 44 | - [1] Represents active Amazon customer accounts, 2020. 45 | - [2] Consumer Net Promoter Score (NPS) Surveys: Conducted by Amazon Pay in 2019 among US, UK, DE, FR, IT, and ES consumers who had used Amazon Pay in the 12 months preceding to the survey launch dates. 46 | - [3] Data from Mobile Checkout Optimization Report, by the Baymard Institute, 2020, commissioned by Amazon Pay. 47 | - [4] Axios Harris Poll 100, Corporate Reputation Rankings, July 2020. 48 | - [5] Profitwell, 2020 49 | - [6] Available for qualified physical goods purchases only. 50 | - [7] For eligible transactions detailed on the [Amazon Pay Customer Agreement](https://pay.amazon.com/help/201212430). 51 | - [8] Not available for Royal Mail in the UK. 52 | 53 | = How to enable Amazon Pay on WooCommerce = 54 | 55 | It only takes a few clicks to enable Amazon Pay on your WooCommerce storefront. 56 | 57 | https://www.youtube.com/watch?v=rYoiEjl5Ek8 58 | 59 | == Installation == 60 | 61 | = Minimum Requirements = 62 | 63 | * WordPress 5.5 or greater 64 | * WooCommerce 4.0 or greater 65 | 66 | = Automatic installation = 67 | 68 | Automatic installation is the easiest option as WordPress handles the file transfers itself and you don’t need to leave your web browser. To do an automatic install of, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New. 69 | 70 | In the search field type "Amazon Pay for WooCommerce" and click Search Plugins. Once you’ve found our plugin you can view details about it such as the point release, rating and description. Most importantly of course, you can install it by simply clicking “Install Now”. 71 | 72 | = Manual installation = 73 | 74 | The manual installation method involves downloading our plugin and uploading it to your webserver via your favorite FTP application. The 75 | WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation). 76 | 77 | = Updating = 78 | 79 | Automatic updates should work like a charm; as always though, ensure you backup your site just in case. 80 | 81 | == Changelog == 82 | 83 | = 2.5.4 - 2025-04-30 = 84 | 85 | * Fix - Error when using a non Standard decimal amount for a currency. 86 | * Fix - Error when phone number was required in Checkout Blocks. 87 | * Fix - Deprecated PHP constants and functions. 88 | * Fix - Pull the Privacy Policy URL from WordPress settings. 89 | * Update - Amazon Pay SDK to v2.6.7. 90 | * Dev - Bumped tested up to WordPress v6.8. 91 | * Dev - Bumped tested up to WooCommerce v9.7.1. 92 | 93 | = 2.5.3 - 2024-09-24 = 94 | 95 | * Fix - Add onboarding text and link 96 | * Fix - Update the plugin Author on wp.org 97 | * Fix - Set Merchant account details selector as toggle 98 | * Fix - Adjust the feedback prompt trigger moment 99 | * Fix - Adjust the plugin description on the WC settings page 100 | * Fix - Check the configuration link removed from actual page 101 | * Dev - Bumped tested up to WordPress v6.6.2. 102 | * Dev - Bumped tested up to WooCommerce v9.3.2. 103 | 104 | = 2.5.2 - 2024-07-25 = 105 | 106 | * Fix - Error when using a non Standard decimal amount for a currency. 107 | * Fix - chargeAmount.Amount is invalid when changing currency after selecting other shipping address. 108 | * Fix - Hide the -OR- on Minicart when selecting "Enable hide button mode". 109 | * Fix - Change Payment Method for a Subscription Paid with another Payment Method. 110 | * Fix - Express Checkout on Product page not getting the values of the variations created by WooCommerce Product Add-Ons and Gravity Forms Product Add-ons 111 | 112 | = 2.5.1 - 2023-10-30 =s 113 | 114 | * Add - New Amazon Pay Platform related Headers. 115 | * Add - Extendable Multi-currency compatibility. 116 | 117 | = 2.5.0 - 2023-07-20 = 118 | 119 | * Add - Plugin review Prompt. 120 | * Fix - Amazon Pay button is displayed on Product pages even when product is Out Of Stock. 121 | * Fix - WooCommerce Blocks compatibility. 122 | * Fix - Limit gateway's frontend settings for blocks 123 | * Update - PHP 8 compatibility fixes. 124 | * Update - Update WP and WC tested up to. 125 | 126 | = 2.4.1 - 2023-02-15 = 127 | 128 | * Fix - Identify if the provided order id refers to an actual order completed through Amazon Pay. 129 | 130 | = 2.4.0 - 2023-01-26 = 131 | 132 | * Update - Bumped required WordPress and WooCommerce versions. 133 | * Fix - Address street missing in billing address (Germany addresses). 134 | * Fix - Activate alexa delivery notifications request format. 135 | * Fix - Allowed currencies population is not taking into account status of compatible multi currency plugin. 136 | * Fix - Cancelled authorizations should mark order as "Pending payment". 137 | * Fix - Compatibility with WooCommerce HPOS (custom order tables). 138 | * Fix - Fatal error when merchant was not migrated to V2 keys. 139 | 140 | = 2.3.0 - 2022-10-11 = 141 | 142 | * Add - Adds estimatedOrderAmount attribute to Amazon Pay button. 143 | * Add - Adds support for Amazon Pay on Cart and Checkout Blocks of WooCommerce Blocks. 144 | * Add - Adds the estimated order amount in every place available by the plugin. 145 | * Fix - If the currency changes while in the FrontEnd the Gateway will alter its availability based on its settings. 146 | * Fix - Warning shouldn't appear on Single product regarding the 'subscriptions_enabled' not being set. 147 | 148 | = 2.2.4 - 2022-08-12 = 149 | 150 | * Fix - Infinite Loop causing Memory Exhaustion. 151 | 152 | = 2.2.3 - 2022-08-12 = 153 | 154 | * Fix - Pick the proper currency when it gets changed by and external multi-currency plugins. 155 | * Fix - Addressed possible fatal errors on widgets page and order pay endpoint. 156 | * Fix - Addressed possible fatal errors when Amazon credentials partially provided. 157 | 158 | = 2.2.2 - 2022-06-17 = 159 | 160 | * Fix - Require phone number only when purchasing physical products. 161 | 162 | = 2.2.1 - 2022-06-13 = 163 | 164 | * Fix - Addresses incorrect gateway availability logic. 165 | 166 | = 2.2.0 - 2022-05-30 = 167 | 168 | * Add - Make Amazon Pay available as a traditional gateway option. 169 | * Add - Support Alexa Delivery notifications. 170 | * Add - Support Amazon Pay "Classic" on the checkout block of WooCommerce blocks. 171 | * Fix - Render Amazon Pay buttons even if they are not visible. 172 | * Fix - Prevents a JavaScript fatal when rendering Amazon Pay button. 173 | * Fix - Make Amazon Pay available for supported currencies only. 174 | * Dev - Bumped tested up to WordPress v6.0. 175 | 176 | = 2.1.3 - 2022-04-11 = 177 | * Fix - Amazon Pay shouldn't be available when not supported currency selected. 178 | * Dev - Bumped tested up to WordPress 5.9. 179 | 180 | = 2.1.2 - 2022-03-17 = 181 | * Fix - Payment fails when site name is longer than 50 characters. 182 | * Fix - Payment fails when recurring payment frequency is passed as an integer. 183 | * Fix - Order changes status to 'Failed' during payment processing. 184 | * Fix - Error opening subscriptions details due to internal errors. 185 | * Fix - Multiple pay buttons showing on shipping method change (thank you gyopiazza). 186 | * Fix - Additional way of identifying order id on return. 187 | 188 | = 2.1.1 - 2022-02-03 = 189 | * Fix - Honoring WooCommerce's setting for decimals when formatting numbers. 190 | * Fix - Formatting numbers won't separate thousands by ','. 191 | 192 | = 2.1.0 - 2022-01-10 = 193 | * Update - Disable option "Hide standard checkout button on cart page" when other payment gateway are activated. 194 | * Fix - Enable subscription amount change support. 195 | * Fix - Accept states without letters mark variations on shipping restriction. 196 | * Fix - Render cart button on update shipping method. 197 | * Add - Process orders created in V1 with V2 handlers. 198 | * Fix - Interference when subscriptions payment method changes to other payment method. 199 | * Fix - Force Decimals to 2 on amounts sent to API to prevent errors on api calls. 200 | * Fix - Save Amazon Reference Id on order _transaction_id order meta field on payment process. 201 | * Update - Hide the API V1 keys on setting when the V2 onboarding is done. 202 | * Fix - Disabling "Hide standard checkout button on cart page" option hides the gateway on the new installations. 203 | * Update - Translation and comments fixes (thank you geist-ahnen, shoheitanaka). 204 | 205 | = 2.0.3 - 2021-06-15 = 206 | * Fix - Issues with state level handling of shipping zones. 207 | * Fix - Issue that attempted to initialize the plugin in the REST API, throwing a fatal error. 208 | * Fix - Issue with subscriptions and checkout session validation, which forced customers to login again. 209 | * Add - Logging when users are asked to log in again, to debug other potential issues with this validation. 210 | 211 | = 2.0.2 - 2021-05-26 = 212 | * Fix - Issue that caused secret key from pre v2 to be lost after migrating to v2. 213 | * Add - Allow recovery of v1 secret key if lost during migration to v2. 214 | 215 | = 2.0.1 - 2021-05-14 = 216 | * Update - WP tested up to 5.7. 217 | * Update - WC tested up to 5.3. 218 | * Fix - Properly compose url for order action buttons. 219 | 220 | = 2.0.0 - 2021-05-11 = 221 | * Upgrade to use the latest Amazon Pay frontend technology and backend API. Functionalities in parity with the previous version. 222 | 223 | = 1.13.1 - 2021-02-25 = 224 | * Fix - Avoid hiding default shipping fields at checkout. 225 | 226 | = 1.13.0 - 2021-02-18 = 227 | * Update - WP tested up to 5.6. 228 | * Update - WC tested up to 5.0. 229 | * Fix - Fatal checkout error when changing subscription's payment method if user is logged out of Amazon account. 230 | * Fix - Checkout error when address book state does not match WooCommerce state data. 231 | * Fix - Multi-currency compatibility is not detected when Price Based on Country and WMPL is active. 232 | * Fix - PHP error when the currencies_supported option is not set. 233 | * Fix - Add InheritShippingAddress to AuthorizeOnBillingAgreement. InheritShippingAddress = True when orders are shipping physical products. 234 | * Fix - Missing order ID in session. 235 | * Fix - Normalize and refactor URL handling when checkout page url is not set. 236 | 237 | = 1.12.2 - 2020-05-05 = 238 | * Fix - Fatal checkout error when submitting orders that do not need shipping. 239 | 240 | = 1.12.1 - 2020-05-04 = 241 | * Update - WC tested up to 4.1. 242 | 243 | = 1.12.0 - 2020-04-20 = 244 | * Add - Automatic key exchange on setup for GB and EU regions. 245 | * Add - Handling for manual encrypted key exchange. 246 | * Add - Pending transactions processed automatically even if the IPN isn't received. 247 | * Add - Additional server-side logging for SCA transactions. 248 | * Update - WC tested up to 4.0. 249 | * Update - WP tested up to 5.4. 250 | * Fix - Transaction timeout handling. 251 | * Fix - Orders are created without billing information. 252 | * Fix - Xero invoice exporting on order creation. 253 | * Fix - Users are created with empty address fields. 254 | 255 | = 1.11.1 - 2020-02-13 = 256 | * Fix - Properly encode URL string 257 | 258 | = 1.11.0 - 2020-01-21 = 259 | * Add - Strong Customer Authentication (SCA) support for subscriptions (billing agreements). 260 | * Add - Support for custom checkout fields. 261 | * Add - Optimal login option to "Login with Amazon" feature. 262 | * Update - Attach WooCommerce and Amazon Pay plugin version as transaction meta data. 263 | * Update - Enable gateway by default and show a warning that it's live. 264 | 265 | = 1.10.3 - 2019-11-18 = 266 | * Update - WC tested up to 3.8. 267 | * Update - WP tested up to 5.3. 268 | 269 | = 1.10.2 - 2019-08-08 = 270 | * Update - WC tested up to 3.7. 271 | 272 | = 1.10.1 - 2019-06-11 = 273 | * Fix - Payment options not working when Amazon Pay v1.10.0 is active 274 | * Fix - Checkout broken when Login with Amazon app is disabled 275 | 276 | = 1.10.0 - 2019-06-03 = 277 | * Add - Strong Customer Authentication (SCA) support for United Kingdom, Euro Region merchants 278 | 279 | = 1.9.1 - 2019-04-17 = 280 | * Tweak - WC tested up to 3.6. 281 | 282 | = 1.9.0 - 2019-02-11 = 283 | * Update - Allow transactions of more than $1000 via async/IPN. 284 | * Update - Upgrade merchant onboarding and registration experience. 285 | * Update - Allow to capture payments in multiple currencies. 286 | * Fix - Avoid using float css property so the cart button is always wrapped by the parent div. 287 | 288 | = 1.8.5 - 2018-10-17 = 289 | * Update - WC tested up to 3.5. 290 | 291 | = 1.8.4 - 2018-05-17 = 292 | * Update - WC tested up to version. 293 | * Update - Privacy policy notification. 294 | * Update - Export/erasure hooks added. 295 | * Fix - Missing most of the address information. 296 | 297 | = 1.8.3 - 2018-05-09 = 298 | * Add - Hook to show/hide amazon address widget "woocommerce_amazon_show_address_widget" (bool), hidden by default. 299 | * Add - New setting field to Enable/Disable Subscriptions support. 300 | * Fix - Compatibility fixes with Advanced Ordernumbers plugin. 301 | * Tweak - Allow Subscription details to be changed for Subscriptions paid through Amazon. 302 | 303 | = 1.8.2 - 2017-03-12 = 304 | * Tweak - Change refund_type string for IPNs when a payment refund is received for subscriptions. 305 | 306 | = 1.8.1 - 2017-12-15 = 307 | * Update - WC tested up to version. 308 | 309 | = 1.8.0 - 2017-11-29 = 310 | * Tweak - Added IPN handlers to handle notifications from Amazon. Currently only add the notification as order notes. 311 | * Tweak - Handle order refund when IPN for payment refund is received. 312 | * Tweak - Added admin notices for conditions that may cause an issue: 1) WooCommerce Germanized is active with disallow cancellation option enabled 2) Shop currency doesn't match with payment region. 313 | * Fix - Remove restriction of subscriptions on EU region. Amazon has reached general availability for the recurring payments product. No white listing needed anymore in any region. 314 | * Fix - Hide customizable button settings if login with Amazon app is disabled. 315 | * Fix - Check city if state is missing from address widget. Please note that StateOrRegion, known as state in WooCommerce, is not mandatory in Amazon address. If the fallback is failed, the workaround would be from shipping zone to target the country. 316 | * Fix - Handles buyer canceled scenario via IPN. 317 | 318 | = 1.7.3 - 2017-07-06 = 319 | * Tweak - Change Payment mark after Amazon re-brand. 320 | * Tweak - Add setting link in plugin action links. 321 | * Fix - Issue in PHP 7.1 might throw an error when trying to checkout. 322 | * Fix - Added proper handler for `AmazonRejected`. It won't render the widgets and redirect to cart immediately. 323 | * Fix - Removed explicit limit check when authorizing billing agreement. Order will be failed when attempting to authorize such payment and subscription still inactive until the order is paid. 324 | * Fix - Suppress coupon notice/form when the transaction is declined with reason code `InvalidPaymentMethod`. 325 | * Fix - PHP Notice: id was called incorrectly when attempting to pay Subscription product. 326 | 327 | = 1.7.2 - 2017-06-27 = 328 | * Add - New Woo plugin header, for WooCommerce 3.1 compatibility. 329 | 330 | = 1.7.1 - 2017-05-01 = 331 | * Fix - Issue where address is not being passed in new order email. 332 | * Fix - Issue where billing and shipping information from Amazon were not saved when login app is not enabled. 333 | * Fix - Make address widget read-only when authorization is declined with reason code `InvalidPaymentMethod`. 334 | 335 | = 1.7.0 - 2017-04-04 = 336 | * Fix - Update for WooCommerce 3.0 compatibility. 337 | * Fix - Issue where subscription renewal order could not find billing agreement ID. 338 | * Tweak - Compability with WPML. 339 | * Fix - issue where disabled guest checkout with generated username and password blocked checkout. 340 | * Tweak - Updated strings "Amazon Pay" as the brand name. 341 | * Fix - Improper handling of declined authorization. 342 | -------------------------------------------------------------------------------- /templates/emails/legacy/hard-decline.php: -------------------------------------------------------------------------------- 1 | 12 | 13 |

,

14 |

15 | 19 |

20 |

,

21 | 22 | -------------------------------------------------------------------------------- /templates/emails/legacy/soft-decline.php: -------------------------------------------------------------------------------- 1 | $url"; 32 | ?> 33 | 34 |

,

35 |

36 | 40 |

41 |

42 |

43 | 47 |

48 |

,

49 | 50 | --------------------------------------------------------------------------------