├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CHANGES.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── NOTICE.txt
├── README.md
├── amazon_pay
├── __init__.py
├── ap_region.py
├── client.py
├── ipn_handler.py
├── login_with_amazon.py
├── lwa_region.py
├── payment_request.py
├── payment_response.py
└── version.py
├── pytest.ini
├── setup.cfg
├── setup.py
└── test
├── log.txt
├── sanlog.txt
├── test.pem
├── test_ap.py
├── test_ipn.py
└── test_lwa.py
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | *.pyo
3 | *.class
4 | *~
5 | *#
6 | /doc/generated/*
7 | /runpy
8 | /build
9 | .coverage
--------------------------------------------------------------------------------
/CHANGES.txt:
--------------------------------------------------------------------------------
1 | Version 2.7.1 - March 2021
2 | - Fixed security risk - Buyer Access token is passed as HTTP header instead of query parameter in URL for get_login_profile API
3 |
4 | Version 2.7.0 - March 2021
5 | - Add new field in ConfirmOrderReference call: expect_immediate_authorization. If this value is set to true, the ORO will auto-close in case no authorize is triggered within 60 minutes after the confirm. Default: None
6 |
7 | Version 2.6.0 - February 2019
8 | - Added new supplementary_data attribute for AuthorizeOnBillingAgreement and CreateOrderReferenceForId.
9 | - Added additional attributes (success_url, failure_url, authorization_amount, currency_code) to ConfirmOrderReference. For usage instructions, please consult the Amazon Pay Strong Customer Authentication (SCA) Upgrade Integration GuideAmazon Pay Strong Customer Authentication (SCA) Upgrade Integration Guide
10 |
11 | Version 2.5.2 September 2018
12 | - Bug Fix XML, JSON and Dictionary responses not returning the propper UTF-8 encoding
13 |
14 | Version 2.5.1 September 2018
15 | - Add support for SupplementaryData attribute in set_order_reference_details, set_order_attributes and get_order_reference_details.
16 | - Refactor unit tests.
17 |
18 | Version 2.5.0 April 2018
19 | - Add support for get_merchant_account_status call.
20 |
21 | Version 2.3.0 November 2017
22 | - Add support for set_order_attributes call.
23 |
24 | Version 2.2.0 August 2017
25 | - Add support for list_order_reference and list_order_reference_by_next_token calls.
26 |
27 | Version 2.1.0 June 2017
28 | - Add access_token variable to get_order_reference_details function.
29 |
30 | Version 2.0.0 February 2017
31 | - Rebrand and User-Agent.
32 |
33 | Version 1.0.2 - Jan 2017
34 | - Enable logging.
35 |
36 | Version 1.0.1 - August 2016
37 | - Add support for IPN handling.
38 |
39 | Version 1.0.0 - May 2015
40 | - Initial Release
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/amzn/amazon-pay-sdk-csharp/issues), or [recently closed](https://github.com/amzn/amazon-pay-sdk-csharp/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/amzn/amazon-pay-sdk-csharp/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/amzn/amazon-pay-sdk-csharp/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use
4 | this file except in compliance with the License. A copy of the License is
5 | located at http://aws.amazon.com/apache2.0/ or in the "license" file
6 | accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT
7 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
8 | License for the specific language governing permissions and limitations under
9 | the License.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > **Important:** This SDK has been deprecated. Amazon Pay will continue to support this version but it will not be updated with new features. New integrations should refer this [documentation](https://developer.amazon.com/docs/amazon-pay/intro.html) for more details.
2 |
3 | ## Synopsis
4 |
5 | The official Amazon Pay Python SDK.
6 |
7 | ## Requirements
8 |
9 | Python >= 3.2
10 | pyOpenSSL >= 0.11
11 | Requests >= 2.6.0
12 |
13 | ## Documentation
14 |
15 | * The Integration steps can be found [here](https://pay.amazon.com/developer/documentation)
16 |
17 | ## Sample
18 |
19 | * View the sample integration demo [here](https://amzn.github.io/amazon-pay-sdk-samples/)
20 |
21 | ## Installation
22 |
23 | ```
24 | $ git clone https://github.com/amzn/amazon-pay-sdk-python.git
25 | $ cd amazon-pay-sdk-python
26 | $ sudo python3 setup.py install
27 | ```
28 |
29 | PyPI
30 | ```
31 | $ sudo pip3 install amazon_pay
32 | ```
33 |
34 | Test it.
35 | ```
36 | $ python3
37 | Python 3.4.0 (default, Apr 11 2014, 13:05:11)
38 | [GCC 4.8.2] on linux
39 | Type "help", "copyright", "credits" or "license" for more information.
40 | >>> from amazon_pay.client import AmazonPayClient
41 | >>>
42 | ```
43 |
44 | If you run into problems related to 'IncompleteRead' try the following.
45 | ```
46 | $ sudo easy_install3 -U pip
47 | ```
48 |
49 | ## Client Code Examples
50 | *This is only a subset of calls. All MWS Amazon Pay API calls are supported.*
51 |
52 | Instantiate the client. The required parameters are mws_access_key,
53 | mws_secret key, merchant_id, region, and currency_code.
54 | *sandbox* sets up if it is in Sandbox or Production mode. If you do not pass
55 | in the required parameters you must set the corresponding environment variable.
56 | See the [client](https://github.com/amzn/amazon-pay-sdk-python/blob/master/amazon_pay/client.py#L31-L67)
57 | documentation for more information.
58 | ```python
59 | from amazon_pay.client import AmazonPayClient
60 |
61 | client = AmazonPayClient(
62 | mws_access_key='YOUR_ACCESS_KEY',
63 | mws_secret_key='YOUR_SECRET_KEY',
64 | merchant_id='YOUR_MERCHANT_ID',
65 | region='na',
66 | currency_code='USD',
67 | sandbox=True)
68 | ```
69 |
70 | GetOrderReferenceDetails
71 | ```python
72 | ret = client.get_order_reference_details(
73 | amazon_order_reference_id='AMAZON_ORDER_REFERENCE_ID',
74 | address_consent_token='ADDRESS_CONSENT_TOKEN')
75 | print(ret.to_json()) # to_xml and to_dict are also valid
76 | ```
77 |
78 | SetOrderReferenceDetails
79 | ```python
80 | ret = client.set_order_reference_details(
81 | amazon_order_reference_id='AMAZON_ORDER_REFERENCE_ID',
82 | order_total='1.00',
83 | seller_note='My seller note.',
84 | seller_order_id='MY_UNIQUE_ORDER_ID',
85 | store_name='My store name.',
86 | custom_information='My custom information.')
87 | print(ret.to_json()) # to_xml and to_dict are also valid
88 | ```
89 |
90 | ConfirmOrderReference
91 | ```python
92 | ret = client.confirm_order_reference(
93 | amazon_order_reference_id='AMAZON_ORDER_REFERENCE_ID')
94 | print(ret.to_json()) # to_xml and to_dict are also valid
95 | ```
96 |
97 | Authorize
98 | ```python
99 | ret = client.authorize(
100 | amazon_order_reference_id='AMAZON_ORDER_REFERENCE_ID',
101 | authorization_reference_id='MY_UNIQUE_AUTHORIZATION_ID',
102 | authorization_amount='1.00',
103 | seller_authorization_note='Authorization note.',
104 | transaction_timeout=10,
105 | capture_now=False)
106 | json_response = ret.to_json()
107 | ```
108 |
109 | GetAuthorizationDetails
110 | ```python
111 | # authorization ID returned from 'Authorize' call.
112 | authorization_id = json.loads(json_response)['AuthorizeResponse'][
113 | 'AuthorizeResult']['AuthorizationDetails']['AmazonAuthorizationId']
114 |
115 | ret = client.get_authorization_details(
116 | amazon_authorization_id=authorization_id)
117 | print(ret.to_json()) # to_xml and to_dict are also valid
118 | ```
119 |
120 | Capture
121 | ```python
122 | # authorization ID returned from 'Authorize' call.
123 | ret = client.capture(
124 | amazon_authorization_id='MY_ATHORIZATION_ID',
125 | capture_reference_id='MY_UNIQUE_CAPTURE_ID',
126 | capture_amount='1.00',
127 | seller_capture_note='Capture note.')
128 | print(ret.to_json()) # to_xml and to_dict are also valid
129 | ```
130 |
131 | GetCaptureDetails
132 | ```python
133 | # capture ID returned from 'Capture' call.
134 | ret = client.get_capture_details(
135 | amazon_capture_id='MY_CAPTURE_ID')
136 | print(ret.to_json()) # to_xml and to_dict are also valid
137 | ```
138 |
139 | Charge - This method combines all the above calls into one which allows you to
140 | set, confirm, authorize, and capture in a single call.
141 | If this is a billing agreement it will first check to see what state it's in
142 | to see if it needs to be set. If already set, it will authorize on the billing
143 | agreement.
144 | ```python
145 | ret = client.charge(
146 | amazon_order_reference_id='ORDER_REFERENCE_ID or BILLING_AGREEMENT_ID',
147 | charge_amount='10.00',
148 | charge_note='MY_CHARGE_NOTE',
149 | authorize_reference_id='MY_UNIQUE_AUTHORIZATION_ID')
150 | print(ret.to_json())
151 | ```
152 |
153 | Logging has been enabled, if you want to have logging output there are 3 ways it
154 | can be used. If you have logging settings you are currently using and you don't
155 | want them to change you can set log_enabled=True and logging output will follow
156 | your defined logging. If you are not familiar with how to setup logging settings
157 | we have pre-defined settings for you to use. In addition to setting log_enabled
158 | to true, if you want the logging output to a file set log_file_name to the name
159 | and location you want logging output to. If no value is provided logging will be
160 | sent to the console. The log_levels that can be set are "CRITICAL"; "ERROR";
161 | "WARNING"; "INFO"; "DEBUG"; "NOTSET". In the SDK, only DEBUG is used.
162 | log_file_name and log_level are set to None by default.
163 | log_enabled is set to False.
164 |
165 | Below is an example of how you can enable logging and output to a file. For
166 | additional settings for client please see the client example above.
167 | ```python
168 | from amazon_pay.client import AmazonPayClient
169 |
170 | client = AmazonPayClient(
171 | mws_access_key=session['mws_access_key'],
172 | mws_secret_key=session['mws_secret_key'],
173 | merchant_id=session['merchant_id'],
174 | sandbox=True,
175 | region='na',
176 | currency_code='USD',
177 | log_enabled=True,
178 | log_file_name="log.txt",
179 | log_level="DEBUG")
180 | ```
181 |
182 | ## Example Responses
183 |
184 | GetOrderReferenceDetails (JSON)
185 | ```json
186 | {
187 | "GetOrderReferenceDetailsResponse": {
188 | "ResponseMetadata": {
189 | "RequestId": "2dfh56f693-0asf-4121-430a-db59e3ec571d"
190 | },
191 | "GetOrderReferenceDetailsResult": {
192 | "OrderReferenceDetails": {
193 | "CreationTimestamp": "2015-03-05T17:56:11.317Z",
194 | "AmazonOrderReferenceId": "S01-0000000-0000000",
195 | "OrderTotal": {
196 | "CurrencyCode": "USD",
197 | "Amount": "100.00"
198 | },
199 | "SellerNote": "My seller note.",
200 | "SellerOrderAttributes": {
201 | "CustomInformation": "My custom information.",
202 | "SellerOrderId": "14553",
203 | "StoreName": "My store name."
204 | },
205 | "ReleaseEnvironment": "Sandbox",
206 | "Buyer": {
207 | "Email": "bob@example.com",
208 | "Name": "Bob"
209 | },
210 | "Destination": {
211 | "DestinationType": "Physical",
212 | "PhysicalDestination": {
213 | "PostalCode": "60602",
214 | "Phone": "800-000-0000",
215 | "Name": "Susie Smith",
216 | "StateOrRegion": "IL",
217 | "AddressLine2": "Suite 2500",
218 | "AddressLine1": "10 Ditka Ave",
219 | "CountryCode": "US",
220 | "City": "Chicago"
221 | }
222 | },
223 | "OrderReferenceStatus": {
224 | "LastUpdateTimestamp": "2015-03-05T17:57:16.233Z",
225 | "State": "Open"
226 | },
227 | "ExpirationTimestamp": "2015-09-01T17:56:11.317Z",
228 | "IdList": {
229 | "member": [
230 | "S01-0000000-0000000-A000000",
231 | "S01-0000000-0000000-A999999"
232 | ]
233 | }
234 | }
235 | }
236 | }
237 | }
238 | ```
239 |
240 | GetOrderReferenceDetails (XML)
241 | ```xml
242 |
243 |
244 |
245 | S01-5835994-2647190
246 | 2015-09-01T17:56:11.317Z
247 | My seller note.
248 |
249 | 100.00
250 | USD
251 |
252 |
253 | S01-0000000-0000000-A000000
254 | S01-0000000-0000000-A999999
255 |
256 |
257 |
258 | 2015-03-05T17:57:16.233Z
259 | Open
260 |
261 |
262 | Physical
263 |
264 | 800-000-0000
265 | 60602
266 | Susie Smith
267 | US
268 | IL
269 | Suite 2500
270 | 10 Ditka Ave
271 | Chicago
272 |
273 |
274 | Sandbox
275 |
276 | bob@example.com
277 | Bob
278 |
279 |
280 | My custom information.
281 | My store name.
282 | 14553
283 |
284 | 2015-03-05T17:56:11.317Z
285 |
286 |
287 |
288 | 6c2a39ce-afb3-492e-8e67-4945a9a63f0e
289 |
290 |
291 | ```
292 |
293 | ## IPN Handler Code Example
294 | Flask
295 | ```python
296 | from flask import request
297 |
298 | @app.route('/ipn_handler', methods=['GET', 'POST'])
299 | def ipn_handler():
300 | from pay_with_amazon.ipn_handler import IpnHandler
301 |
302 | ret = IpnHandler(request.data, request.headers)
303 | if ret.authenticate():
304 | return(ret.to_json())
305 | else:
306 | return(ret.error)
307 | ```
308 | Response
309 | ```json
310 | {
311 | "OrderReferenceNotification": {
312 | "OrderReference": {
313 | "OrderTotal": {
314 | "CurrencyCode": "USD",
315 | "Amount": "0.0"
316 | },
317 | "CreationTimestamp": "2013-01-01T01:01:01.001Z",
318 | "OrderReferenceStatus": {
319 | "State": "Closed",
320 | "ReasonCode": "AmazonClosed",
321 | "LastUpdateTimestamp": "2013-01-01T01:01:01.001Z"
322 | },
323 | "SellerOrderAttributes": null,
324 | "AmazonOrderReferenceId": "P01-0000000-0000000-A000000",
325 | "ExpirationTimestamp": "2013-01-01T01:01:01.001Z"
326 | }
327 | }
328 | }
329 | ```
330 |
331 | ## Search for Orders
332 |
333 | ListOrderReference
334 | ```python
335 | # This method returns a list of all orders made with the custom ID tag attached
336 | # on each order usually the SellerOrderId.
337 | # For query, you'll want to enter in the tag you wish to search.
338 | # For query_type, currently only SellerOrderId is accepted at this time.
339 | # However, more query types will become available in the future.
340 | ret = client.list_order_reference(
341 | query_id="MY_QUERY_ID",
342 | query_type="MY_QUERY_TYPE")
343 | print(ret.to_json())
344 | ```
345 |
346 | Response
347 | ```json
348 | {
349 | "ListOrderReferenceResponse": {
350 | "ListOrderReferenceResult": {
351 | "OrderReferenceList": {
352 | "OrderReference": {
353 | "ReleaseEnvironment": "Sandbox",
354 | "OrderReferenceStatus": {
355 | "LastUpdateTimestamp": "2017-08-10T21:25:38.628Z",
356 | "State": "Open"
357 | },
358 | "AmazonOrderReferenceId": "S01-0000000-0000000",
359 | "CreationTimestamp": "2017-08-10T21:25:10.592Z",
360 | "SellerOrderAttributes": {
361 | "StoreName": "Test Store Name",
362 | "CustomInformation": "Example Customer Info",
363 | "OrderItemCategories": {
364 | "OrderItemCategory": "Antiques"
365 | },
366 | "SellerOrderId": "QUERY_ID"
367 | },
368 | "OrderTotal": {
369 | "CurrencyCode": "USD",
370 | "Amount": "12.00"
371 | }
372 | }
373 | }
374 | },
375 | "ResponseMetadata": {
376 | "RequestId": "fbd130c0-fc7e-46ca-8d97-248f89c16a1e"
377 | }
378 | }
379 | }
380 | ```
381 |
382 | ListOrderReferenceByNextToken
383 | ```python
384 | # This method returns a list of the continued orders from the previous call
385 | # using a NextPageToken value to render the next page of data if a page_size
386 | # was used to split the list of orders into multiple pages.
387 | reply = client.list_order_reference_by_next_token(
388 | next_page_token="NEXT_PAGE_TOKEN")
389 | print(ret.to_json())
390 | ```
391 |
392 | ## Show the Entire Payment History of an Order
393 |
394 | GetPaymentDetails
395 | ```python
396 | # This method returns the entire payment history of an order in an easy to
397 | # parse format of a list of objects .
398 |
399 | reply = client.get_payment_details(amazon_order_reference_id='AMAZON_ORDER_REFERENCE_ID')
400 | ```
401 |
402 | Response
403 | ```python
404 | [,
405 | ,
406 | ]
407 | ```
408 | You can convert this data using our to_dict(), to_json(), or to_xml()
409 | and then parse through the response of each item. The example below
410 | shows the basic way to parse through these objects. The example below when
411 | paired with the above information will return the corresponding hex
412 | object as well as all of the information inside of it. This is to
413 | show what is stored, and how you can read through the data.
414 | ```python
415 | for i in range(len(reply)):
416 | query = json.loads(reply[i].to_json())
417 | if 'GetOrderReferenceDetailsResponse' in query:
418 | print(reply[i])
419 | print(query['GetOrderReferenceDetailsResponse'])
420 | elif 'GetAuthorizationDetailsResponse' in query:
421 | print(reply[i])
422 | print(query['GetAuthorizationDetailsResponse'])
423 | elif 'GetCaptureDetailsResponse' in query:
424 | print(reply[i])
425 | print(query['GetCaptureDetailsResponse'])
426 | elif 'GetRefundDetailsResponse' in query:
427 | print(reply[i])
428 | print(query['GetRefundDetailsResponse'])
429 | else:
430 | print("Error")
431 | ```
432 |
433 | ## API Reference
434 |
435 | [Official Amazon Pay API Reference](https://pay.amazon.com/developer/documentation)
436 |
437 |
438 |
--------------------------------------------------------------------------------
/amazon_pay/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amzn/amazon-pay-sdk-python/fb26c7950b0c269783476cc221f540ce266bf8ed/amazon_pay/__init__.py
--------------------------------------------------------------------------------
/amazon_pay/ap_region.py:
--------------------------------------------------------------------------------
1 | regions = {'uk': 'mws-eu.amazonservices.com',
2 | 'de': 'mws-eu.amazonservices.com',
3 | 'eu': 'mws-eu.amazonservices.com',
4 | 'us': 'mws.amazonservices.com',
5 | 'na': 'mws.amazonservices.com',
6 | 'jp': 'mws.amazonservices.jp'}
7 |
--------------------------------------------------------------------------------
/amazon_pay/client.py:
--------------------------------------------------------------------------------
1 | import re
2 | import os
3 | import sys
4 | import json
5 | import logging
6 | import platform
7 | import amazon_pay.ap_region as ap_region
8 | import amazon_pay.version as ap_version
9 | from amazon_pay.payment_request import PaymentRequest
10 | from fileinput import filename
11 |
12 | class AmazonPayClient:
13 |
14 | logger = logging.getLogger('__amazon_pay_sdk__')
15 | logger.addHandler(logging.NullHandler())
16 |
17 | """This client allows you to make all the necessary API calls to
18 | integrate with Amazon Pay.
19 | """
20 | # pylint: disable=too-many-instance-attributes, too-many-public-methods
21 | # pylint: disable=too-many-arguments, too-many-lines
22 |
23 | def __init__(
24 | self,
25 | mws_access_key=None,
26 | mws_secret_key=None,
27 | merchant_id=None,
28 | region=None,
29 | currency_code=None,
30 | sandbox=False,
31 | handle_throttle=True,
32 | application_name=None,
33 | application_version=None,
34 | log_enabled=False,
35 | log_file_name=None,
36 | log_level=None):
37 |
38 | """
39 | Parameters
40 | ----------
41 | mws_access_key : string, optional
42 | Your MWS access key. If no value is passed, check environment.
43 | Environment variable: AP_MWS_ACCESS_KEY
44 | (mws_access_key must be passed or specified in environment or this
45 | will result in an error)
46 |
47 | mws_secret_key : string, optional
48 | Your MWS secret key. If no value is passed, check environment.
49 | Environment variable: AP_MWS_SECRET_KEY
50 | (mws_secret_key must be passed or specified in environment or this
51 | will result in an error)
52 |
53 | merchant_id : string, optional
54 | Your merchant ID. If you are a marketplace enter the seller's merchant
55 | ID. If no value is passed, check environment.
56 | Environment variable: AP_MERCHANT_ID
57 | (merchant_id must be passed or specified in environment or this
58 | will result in an error)
59 |
60 | region : string, optional
61 | The region in which you are conducting business. If no value is
62 | passed, check environment.
63 | Environment variable: AP_REGION
64 | (region must be passed or specified in environment or this
65 | will result in an error)
66 |
67 | sandbox : string, optional
68 | Toggle sandbox mode. Default: False.
69 |
70 | currency_code: string, required
71 | Currency code for your region.
72 | Environment variable: AP_CURRENCY_CODE
73 |
74 | handle_throttle: boolean, optional
75 | If requests are throttled, do you want this client to pause and
76 | retry? Default: True
77 |
78 | application_name: string, optional
79 | The name of your application. This will get set in the UserAgent.
80 | Default: None
81 |
82 | application_version: string, optional
83 | Your application version. This will get set in the UserAgent.
84 | Default: None
85 |
86 | log_file_name: string, optional
87 | The name of the file for logging
88 | Default: None
89 |
90 | log_level: integer, optional
91 | The level of logging recorded
92 | Default: "None"
93 | Levels: "CRITICAL"; "ERROR"; "WARNING"; "INFO"; "DEBUG"; "NOTSET"
94 | """
95 | env_param_map = {'mws_access_key': 'AP_MWS_ACCESS_KEY',
96 | 'mws_secret_key': 'AP_MWS_SECRET_KEY',
97 | 'merchant_id': 'AP_MERCHANT_ID',
98 | 'region': 'AP_REGION',
99 | 'currency_code': 'AP_CURRENCY_CODE'}
100 | for param in env_param_map:
101 | if eval(param) is None:
102 | try:
103 | setattr(self, param, os.environ[env_param_map[param]])
104 | except:
105 | raise ValueError('Invalid {}.'.format(param))
106 | else:
107 | setattr(self, param, eval(param))
108 |
109 | try:
110 | self._region = ap_region.regions[self.region]
111 | # used for Login with Amazon helper
112 | self._region_code = self.region
113 | except KeyError:
114 | raise KeyError('Invalid region code ({})'.format(self.region))
115 |
116 | self.mws_access_key = self.mws_access_key
117 | self.mws_secret_key = self.mws_secret_key
118 | self.merchant_id = self.merchant_id
119 | self.currency_code = self.currency_code
120 | self.handle_throttle = handle_throttle
121 | self.application_name = application_name
122 | self.application_version = application_version
123 |
124 | self._sandbox = sandbox
125 | self._api_version = ap_version.versions['api_version']
126 | self._application_library_version = ap_version.versions[
127 | 'application_version']
128 | self._mws_endpoint = None
129 | self._set_endpoint()
130 |
131 | if log_enabled is not False:
132 | numeric_level = getattr(logging, log_level.upper(), None)
133 | if numeric_level is not None:
134 | if log_file_name is not None:
135 | self.logger.setLevel(numeric_level)
136 | fh = logging.FileHandler(log_file_name)
137 | self.logger.addHandler(fh)
138 | fh.setLevel(numeric_level)
139 | else:
140 | self.logger.setLevel(numeric_level)
141 | ch = logging.StreamHandler(sys.stdout)
142 | self.logger.addHandler(ch)
143 | ch.setLevel(numeric_level)
144 |
145 | app_name_and_ver = ''
146 |
147 | if application_name not in ['', None]:
148 | app_name_and_ver = app_name_and_ver + str(application_name)
149 | if application_version not in ['', None]:
150 | app_name_and_ver = app_name_and_ver + '/' + str(application_version)
151 |
152 | elif application_version not in ['', None]:
153 | app_name_and_ver = app_name_and_ver + str(application_version)
154 |
155 | if ((application_name not in ['', None]) | (application_version not in ['', None])):
156 | app_name_and_ver = app_name_and_ver + '; '
157 |
158 | current_py_ver = ".".join(map(str, sys.version_info[:3]))
159 |
160 | self._user_agent = 'amazon-pay-sdk-python/{0} ({1}Python/{2}; {3}/{4})'.format(
161 | str(self._application_library_version),
162 | str(app_name_and_ver),
163 | str(current_py_ver),
164 | str(platform.system()),
165 | str(platform.release())
166 | )
167 |
168 | self.logger.debug('user agent: %s', self._user_agent)
169 |
170 | self._headers = {
171 | 'Content-Type': 'application/x-www-form-urlencoded',
172 | 'User-Agent': self._user_agent}
173 |
174 | @property
175 | def sandbox(self):
176 | return self._sandbox
177 |
178 | @sandbox.setter
179 | def sandbox(self, value):
180 | """Set Sandbox mode"""
181 | self._sandbox = value
182 | self._set_endpoint()
183 |
184 | def _set_endpoint(self):
185 | """Set endpoint for API calls"""
186 | if self._sandbox:
187 | self._mws_endpoint = \
188 | 'https://{}/OffAmazonPayments_Sandbox/{}'.format(
189 | self._region, self._api_version)
190 | else:
191 | self._mws_endpoint = \
192 | 'https://{}/OffAmazonPayments/{}'.format(
193 | self._region, self._api_version)
194 |
195 | def get_login_profile(self, access_token, client_id):
196 | """Get profile associated with LWA user. This is a helper method for
197 | Login with Amazon (separate service). Added here for convenience.
198 | """
199 | from amazon_pay.login_with_amazon import LoginWithAmazon
200 | lwa_client = LoginWithAmazon(
201 | client_id=client_id,
202 | region=self._region_code,
203 | sandbox=self._sandbox)
204 | response = lwa_client.get_login_profile(access_token=access_token)
205 | return response
206 |
207 | def get_merchant_account_status(
208 | self,
209 | merchant_id=None,
210 | mws_auth_token=None):
211 | """ Check the account status of the merchant
212 |
213 | Parameters
214 | ----------
215 | merchant_id : string, optional
216 | Your merchant ID. If you are a marketplace enter the seller's merchant
217 | ID.
218 |
219 | mws_auth_token: string, optional
220 | Your marketplace web service auth token. Default: None
221 |
222 | """
223 | parameters = {
224 | 'Action': 'GetMerchantAccountStatus'
225 | }
226 |
227 | optionals = {
228 | 'SellerId': merchant_id,
229 | 'MWSAuthToken': mws_auth_token}
230 |
231 | return self._operation(params=parameters, options=optionals)
232 |
233 | def create_order_reference_for_id(
234 | self,
235 | object_id,
236 | object_id_type,
237 | order_total,
238 | inherit_shipping_address=True,
239 | confirm_now=False,
240 | platform_id=None,
241 | seller_note=None,
242 | seller_order_id=None,
243 | store_name=None,
244 | custom_information=None,
245 | merchant_id=None,
246 | mws_auth_token=None,
247 | supplementary_data=None):
248 | # pylint: disable=too-many-arguments
249 | """Creates an order reference for the given object.
250 |
251 | Parameters
252 | ----------
253 | object_id : string, required
254 | The identifier of the object to be used to create an order reference.
255 |
256 | object_id_type : string, required
257 | The type of the object represented by the Id request parameter.
258 |
259 | order_total : string, required
260 | Specifies the total amount of the order represented by this order
261 | reference.
262 |
263 | inherit_shipping_address : boolean, optional
264 | Specifies whether to inherit the shipping address details from the
265 | object represented by the Id request parameter. Default: True
266 |
267 | confirm_now : boolean, optional
268 | Indicates whether to directly confirm the requested order reference.
269 | Default: False
270 |
271 | merchant_id : string, required
272 | Your merchant ID. If you are a marketplace enter the seller's merchant
273 | ID.
274 |
275 | mws_auth_token: string, optional
276 | Your marketplace web service auth token. Default: None
277 |
278 | supplementary_data: string, optional
279 | Only use if instructed to do so by Amazon Pay. JSON string. Default: None
280 |
281 | """
282 | parameters = {
283 | 'Action': 'CreateOrderReferenceForId',
284 | 'Id': object_id,
285 | 'IdType': object_id_type,
286 | 'OrderReferenceAttributes.OrderTotal.Amount': order_total,
287 | 'OrderReferenceAttributes.OrderTotal.CurrencyCode': self.currency_code}
288 | optionals = {
289 | 'InheritShippingAddress': str(inherit_shipping_address).lower(),
290 | 'ConfirmNow': str(confirm_now).lower(),
291 | 'OrderReferenceAttributes.PlatformId': platform_id,
292 | 'OrderReferenceAttributes.SellerNote': seller_note,
293 | 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId': seller_order_id,
294 | 'OrderReferenceAttributes.SellerOrderAttributes.StoreName': store_name,
295 | 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation': custom_information,
296 | 'SellerId': merchant_id,
297 | 'OrderReferenceAttributes.SupplementaryData': supplementary_data,
298 | 'MWSAuthToken': mws_auth_token}
299 |
300 | return self._operation(params=parameters, options=optionals)
301 |
302 | def get_billing_agreement_details(
303 | self,
304 | amazon_billing_agreement_id,
305 | address_consent_token=None,
306 | merchant_id=None,
307 | mws_auth_token=None):
308 | """Returns details about the Billing Agreement object and its current
309 | state.
310 |
311 | Parameters
312 | ----------
313 | amazon_billing_agreement_id : string, required
314 | The billing agreement identifier.
315 |
316 | address_consent_token : string, optional
317 | The buyer address consent token. You must provide a valid
318 | AddressConsentToken if you want to get the full shipping address
319 | before the billing agreement is confirmed. Otherwise you will only
320 | receive the city, state, postal code, and country before you confirm
321 | the billing agreement. Default: None
322 |
323 | merchant_id : string, required
324 | Your merchant ID. If you are a marketplace enter the seller's merchant
325 | ID.
326 |
327 | mws_auth_token: string, optional
328 | Your marketplace web service auth token. Default: None
329 | """
330 | parameters = {
331 | 'Action': 'GetBillingAgreementDetails',
332 | 'AmazonBillingAgreementId': amazon_billing_agreement_id}
333 | optionals = {
334 | 'AddressConsentToken': address_consent_token,
335 | 'SellerId': merchant_id,
336 | 'MWSAuthToken': mws_auth_token}
337 | return self._operation(params=parameters, options=optionals)
338 |
339 | def set_billing_agreement_details(
340 | self,
341 | amazon_billing_agreement_id,
342 | platform_id=None,
343 | seller_note=None,
344 | seller_billing_agreement_id=None,
345 | store_name=None,
346 | custom_information=None,
347 | merchant_id=None,
348 | mws_auth_token=None):
349 | # pylint: disable=too-many-arguments
350 | """Sets billing agreement details such as a description of the agreement
351 | and other information about the seller.
352 |
353 | Parameters
354 | ----------
355 | amazon_billing_agreement_id : string, required
356 | The billing agreement identifier.
357 |
358 | platform_id : string, optional
359 | Represents the SellerId of the Solution Provider that developed the
360 | platform. This value should only be provided by solution providers.
361 | It should not be provided by sellers creating their own custom
362 | integration.
363 |
364 | seller_note : string, optional
365 | Represents a description of the billing agreement that is displayed
366 | in emails to the buyer.
367 |
368 | seller_billing_agreement_id : string, optional
369 | The seller-specified identifier of this billing agreement.
370 |
371 | store_name : string, optional
372 | The identifier of the store from which the order was placed. This
373 | overrides the default value in Seller Central under Settings >
374 | Account Settings. It is displayed to the buyer in the email they
375 | receive from Amazon and also in their transaction history on the
376 | Amazon Pay website.
377 |
378 | custom_information : string, optional
379 | Any additional information you wish to include with this billing
380 | agreement.
381 |
382 | merchant_id : string, required
383 | Your merchant ID. If you are a marketplace enter the seller's merchant
384 | ID.
385 |
386 | mws_auth_token: string, optional
387 | Your marketplace web service auth token. Default: None
388 | """
389 | parameters = {
390 | 'Action': 'SetBillingAgreementDetails',
391 | 'AmazonBillingAgreementId': amazon_billing_agreement_id}
392 | optionals = {
393 | 'BillingAgreementAttributes.PlatformId': platform_id,
394 | 'BillingAgreementAttributes.SellerNote': seller_note,
395 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.SellerBillingAgreementId': seller_billing_agreement_id,
396 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.StoreName': store_name,
397 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.CustomInformation': custom_information,
398 | 'SellerId': merchant_id,
399 | 'MWSAuthToken': mws_auth_token}
400 | return self._operation(params=parameters, options=optionals)
401 |
402 | def confirm_billing_agreement(
403 | self,
404 | amazon_billing_agreement_id,
405 | merchant_id=None,
406 | mws_auth_token=None):
407 | """Confirms that the billing agreement is free of constraints and all
408 | required information has been set on the billing agreement.
409 |
410 | Parameters
411 | ----------
412 | amazon_billing_agreement_id : string, required
413 | The billing agreement identifier.
414 |
415 | merchant_id : string, required
416 | Your merchant ID. If you are a marketplace enter the seller's merchant
417 | ID.
418 |
419 | mws_auth_token: string, optional
420 | Your marketplace web service auth token. Default: None
421 | """
422 | parameters = {
423 | 'Action': 'ConfirmBillingAgreement',
424 | 'AmazonBillingAgreementId': amazon_billing_agreement_id}
425 | optionals = {
426 | 'SellerId': merchant_id,
427 | 'MWSAuthToken': mws_auth_token}
428 | return self._operation(params=parameters, options=optionals)
429 |
430 | def validate_billing_agreement(
431 | self,
432 | amazon_billing_agreement_id,
433 | merchant_id=None,
434 | mws_auth_token=None):
435 | """Validates the status of the BillingAgreement object and the payment
436 | method associated with it.
437 |
438 | Parameters
439 | ----------
440 | amazon_billing_agreement_id : string, required
441 | The billing agreement identifier.
442 |
443 | merchant_id : string, required
444 | Your merchant ID. If you are a marketplace enter the seller's merchant
445 | ID.
446 |
447 | mws_auth_token: string, optional
448 | Your marketplace web service auth token. Default: None
449 | """
450 | parameters = {
451 | 'Action': 'ValidateBillingAgreement',
452 | 'AmazonBillingAgreementId': amazon_billing_agreement_id}
453 | optionals = {
454 | 'SellerId': merchant_id,
455 | 'MWSAuthToken': mws_auth_token}
456 | return self._operation(params=parameters, options=optionals)
457 |
458 | def authorize_on_billing_agreement(
459 | self,
460 | amazon_billing_agreement_id,
461 | authorization_reference_id,
462 | authorization_amount,
463 | seller_authorization_note=None,
464 | transaction_timeout=1440,
465 | capture_now=False,
466 | soft_descriptor=None,
467 | seller_note=None,
468 | platform_id=None,
469 | seller_order_id=None,
470 | store_name=None,
471 | custom_information=None,
472 | inherit_shipping_address=True,
473 | merchant_id=None,
474 | mws_auth_token=None,
475 | supplementary_data=None):
476 | # pylint: disable=too-many-arguments
477 | """Reserves a specified amount against the payment method(s) stored in
478 | the billing agreement.
479 |
480 | Parameters
481 | ----------
482 | amazon_billing_agreement_id : string, required
483 | The billing agreement identifier.
484 |
485 | authorization_reference_id : string, required
486 | The identifier for this authorization transaction that you specify.
487 | This identifier must be unique for all your transactions
488 | (authorization, capture, refund, etc.).
489 |
490 | authorization_amount : string, required
491 | Represents the amount to be authorized.
492 |
493 | seller_authorization_note : string, optional
494 | A description for the transaction that is displayed in emails to the
495 | buyer. Default: None
496 |
497 | transaction_timeout : unsigned integer, optional
498 | The number of minutes after which the authorization will
499 | automatically be closed and you will not be able to capture funds
500 | against the authorization. Default: 1440
501 |
502 | capture_now : boolean, optional
503 | Indicates whether to directly capture the amount specified by the
504 | AuthorizationAmount request parameter against an order reference
505 | (without needing to call Capture and without waiting until the order
506 | ships). The captured amount is disbursed to your account in the next
507 | disbursement cycle. Default: False
508 |
509 | seller_note : string, optional
510 | Represents a description of the order that is displayed in emails to
511 | the buyer. Default: None
512 |
513 | platform_id : string, optional
514 | Represents the SellerId of the Solution Provider that developed the
515 | platform. This value should only be provided by Solution Providers.
516 | It should not be provided by sellers creating their own custom
517 | integration. Default: None
518 |
519 | seller_order_id : string, optional
520 | The seller-specified identifier of this order. This is displayed to
521 | the buyer in the email they receive from Amazon and transaction
522 | history on the Amazon Pay website. Default: None
523 |
524 | store_name : string, optional
525 | The identifier of the store from which the order was placed. This
526 | overrides the default value in Seller Central under Settings >
527 | Account Settings. It is displayed to the buyer in the email they
528 | receive from Amazon and also in their transaction history on the
529 | Amazon Pay website. Default: None
530 |
531 | custom_information : string, optional
532 | Any additional information you wish to include with this order
533 | reference. Default: None
534 |
535 | inherit_shipping_address : boolean, optional
536 | Specifies whether to inherit the shipping address details from the
537 | object represented by the Id request parameter. Default: True
538 |
539 | supplementary_data: string, optional
540 | Only use if instructed to do so by Amazon Pay. JSON string. Default: None
541 |
542 | merchant_id : string, required
543 | Your merchant ID. If you are a marketplace enter the seller's merchant
544 | ID.
545 |
546 | mws_auth_token: string, optional
547 | Your marketplace web service auth token. Default: None
548 | """
549 | parameters = {
550 | 'Action': 'AuthorizeOnBillingAgreement',
551 | 'AmazonBillingAgreementId': amazon_billing_agreement_id,
552 | 'TransactionTimeout': transaction_timeout,
553 | 'AuthorizationReferenceId': authorization_reference_id,
554 | 'AuthorizationAmount.Amount': authorization_amount,
555 | 'AuthorizationAmount.CurrencyCode': self.currency_code}
556 | optionals = {
557 | 'CaptureNow': str(capture_now).lower(),
558 | 'SellerAuthorizationNote': seller_authorization_note,
559 | 'SoftDescriptor': soft_descriptor,
560 | 'SellerNote': seller_note,
561 | 'PlatformId': platform_id,
562 | 'InheritShippingAddress': str(inherit_shipping_address).lower(),
563 | 'SellerOrderAttributes.SellerOrderId': seller_order_id,
564 | 'SellerOrderAttributes.StoreName': store_name,
565 | 'SellerOrderAttributes.CustomInformation': custom_information,
566 | 'SellerOrderAttributes.SupplementaryData': supplementary_data,
567 | 'SellerId': merchant_id,
568 | 'MWSAuthToken': mws_auth_token}
569 | return self._operation(params=parameters, options=optionals)
570 |
571 | def close_billing_agreement(
572 | self,
573 | amazon_billing_agreement_id,
574 | closure_reason=None,
575 | merchant_id=None,
576 | mws_auth_token=None):
577 | """Confirms that you want to terminate the billing agreement with the
578 | buyer and that you do not expect to create any new order references or
579 | authorizations on this billing agreement.
580 |
581 | Parameters
582 | ----------
583 | amazon_billing_agreement_id : string, required
584 | The billing agreement identifier.
585 |
586 | closure_reason : string, optional
587 | Describes the reason for closing the billing agreement.
588 | Default: None
589 |
590 | merchant_id : string, required
591 | Your merchant ID. If you are a marketplace enter the seller's merchant
592 | ID.
593 |
594 | mws_auth_token: string, optional
595 | Your marketplace web service auth token. Default: None
596 | """
597 | parameters = {
598 | 'Action': 'CloseBillingAgreement',
599 | 'AmazonBillingAgreementId': amazon_billing_agreement_id}
600 | optionals = {
601 | 'ClosureReason': closure_reason,
602 | 'SellerId': merchant_id,
603 | 'MWSAuthToken': mws_auth_token}
604 | return self._operation(params=parameters, options=optionals)
605 |
606 | def set_order_reference_details(
607 | self,
608 | amazon_order_reference_id,
609 | order_total,
610 | platform_id=None,
611 | seller_note=None,
612 | seller_order_id=None,
613 | store_name=None,
614 | custom_information=None,
615 | merchant_id=None,
616 | mws_auth_token=None,
617 | supplementary_data=None):
618 | """Sets order reference details such as the order total and a
619 | description for the order.
620 |
621 | Parameters
622 | ----------
623 | amazon_order_reference_id : string, required
624 | The order reference identifier retrieved from the amazon pay Button
625 | widget.
626 |
627 | order_total : string, required
628 | Specifies the total amount of the order represented by this order
629 | reference.
630 |
631 | platform_id : string, optional
632 | Represents the SellerId of the Solution Provider that developed the
633 | platform. This value should only be provided by Solution Providers.
634 | It should not be provided by sellers creating their own custom
635 | integration. Default: None
636 |
637 | seller_note : string, optional
638 | Represents a description of the order that is displayed in emails
639 | to the buyer. Default: None
640 |
641 | seller_order_id : string, optional
642 | The seller-specified identifier of this order. This is displayed to
643 | the buyer in the email they receive from Amazon and also in their
644 | transaction history on the Amazon Pay website. Default: None
645 |
646 | store_name : string, optional
647 | The identifier of the store from which the order was placed. This
648 | overrides the default value in Seller Central under Settings >
649 | Account Settings. It is displayed to the buyer in the email they
650 | receive from Amazon and also in their transaction history on the
651 | Amazon Pay website. Default: None
652 |
653 | custom_information : string, optional
654 | Any additional information you wish to include with this order
655 | reference. Default: None
656 |
657 | merchant_id : string, required
658 | Your merchant ID. If you are a marketplace enter the seller's merchant
659 | ID.
660 |
661 | mws_auth_token: string, optional
662 | Your marketplace web service auth token. Default: None
663 |
664 | supplementary_data: string, optional
665 | Only use if instructed to do so by Amazon Pay. JSON string. Default: None
666 | """
667 | parameters = {
668 | 'Action': 'SetOrderReferenceDetails',
669 | 'AmazonOrderReferenceId': amazon_order_reference_id,
670 | 'OrderReferenceAttributes.OrderTotal.Amount': order_total,
671 | 'OrderReferenceAttributes.OrderTotal.CurrencyCode': self.currency_code}
672 | optionals = {
673 | 'OrderReferenceAttributes.PlatformId': platform_id,
674 | 'OrderReferenceAttributes.SellerNote': seller_note,
675 | 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId': seller_order_id,
676 | 'OrderReferenceAttributes.SellerOrderAttributes.StoreName': store_name,
677 | 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation': custom_information,
678 | 'OrderReferenceAttributes.SellerOrderAttributes.SupplementaryData': supplementary_data,
679 | 'SellerId': merchant_id,
680 | 'MWSAuthToken': mws_auth_token}
681 | return self._operation(params=parameters, options=optionals)
682 |
683 | def set_order_attributes(
684 | self,
685 | amazon_order_reference_id,
686 | currency_code=None,
687 | amount=None,
688 | seller_order_id=None,
689 | payment_service_provider_id=None,
690 | payment_service_provider_order_id=None,
691 | platform_id=None,
692 | seller_note=None,
693 | request_payment_authorization=None,
694 | store_name=None,
695 | list_order_item_categories=None,
696 | custom_information=None,
697 | merchant_id=None,
698 | mws_auth_token=None,
699 | supplementary_data=None):
700 | '''
701 | Return and update the information of an order with missing
702 | or updated information
703 |
704 | Parameters
705 | ----------
706 | amazon_order_reference_id : string, required
707 | The order reference identifier retrieved from the amazon pay Button
708 | widget.
709 |
710 | merchant_id : string, optional
711 | Your merchant ID. If you are a marketplace enter the seller's merchant
712 | ID.
713 |
714 | mws_auth_token: string, optional
715 | Your marketplace web service auth token. Default: None
716 |
717 | currency_code : string, optional
718 | The currency you're accepting the order in. A three-digit
719 | currency code, formatted based on the ISO 4217 standard.
720 | Default: None
721 |
722 | amount : string, optional
723 | Specifies the total amount of the order represented by this order
724 | reference. Default: None
725 |
726 | seller_order_id : string, optional
727 | The seller-specified identifier of this order. This is displayed to
728 | the buyer in the email they receive from Amazon and also in their
729 | transaction history on the Amazon Pay website. Default: None
730 |
731 | payment_service_provider_id : string, optional
732 | For use with a Payment Service Provider. This is their specific
733 | ID that is associated with Amazon Pay accounts and services.
734 | Default: None
735 |
736 | payment_service_provider_order_id : string, optional
737 | For use with a Payment Service Provider. This is their specific
738 | ID that is linked to your specific Amazon Pay Order Reference ID.
739 | Default: None
740 |
741 | platform_id : string, optional
742 | Represents the SellerId of the Solution Provider that developed the
743 | platform. This value should only be provided by Solution Providers.
744 | It should not be provided by sellers creating their own custom
745 | integration. Default: None
746 |
747 | seller_note : string, optional
748 | Represents a description of the order that is displayed in
749 | e-mails to the buyer. Default: None
750 |
751 | request_payment_authorization : boolean (string), optional
752 | Specifies if the merchants want their buyers to go through
753 | multi-factor authentication. Default: None
754 |
755 | store_name : string, optional
756 | The identifier of the store from which the order was placed. This
757 | overrides the default value in Seller Central under Settings >
758 | Account Settings. It is displayed to the buyer in the email they
759 | receive from Amazon and also in their transaction history on the
760 | Amazon Pay website. Default: None
761 |
762 | list_order_item_categories : list (string), optional
763 | List the category, or categories, that correlate to the order
764 | in question. You may set more than one item. Default: None
765 |
766 | custom_information : string, optional
767 | Any additional information you want your back-end system to
768 | keep record of. Your customers will not see this, and this
769 | will not be visible on Seller Central. This can only be
770 | accessed if your back end system supports calling this variable.
771 | Default: None
772 |
773 | supplementary_data: string, optional
774 | Only use if instructed to do so by Amazon Pay. JSON string. Default: None
775 | '''
776 |
777 | parameters = {
778 | 'Action': 'SetOrderAttributes',
779 | 'AmazonOrderReferenceId': amazon_order_reference_id
780 | }
781 |
782 | optionals = {
783 | 'OrderAttributes.OrderTotal.Amount': amount,
784 | 'OrderAttributes.OrderTotal.CurrencyCode': currency_code,
785 | 'OrderAttributes.SellerOrderAttributes.CustomInformation': custom_information,
786 | 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId':
787 | payment_service_provider_id,
788 | 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId':
789 | payment_service_provider_order_id,
790 | 'OrderAttributes.PlatformId': platform_id,
791 | 'OrderAttributes.RequestPaymentAuthorization': request_payment_authorization,
792 | 'OrderAttributes.SellerNote': seller_note,
793 | 'OrderAttributes.SellerOrderAttributes.SellerOrderId': seller_order_id,
794 | 'OrderAttributes.SellerOrderAttributes.StoreName': store_name,
795 | 'OrderAttributes.SellerOrderAttributes.SupplementaryData': supplementary_data,
796 | 'SellerId': merchant_id,
797 | 'MWSAuthToken': mws_auth_token
798 | }
799 |
800 | if list_order_item_categories is not None:
801 | self._enumerate(
802 | 'OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.',
803 | list_order_item_categories, optionals)
804 |
805 | return self._operation(params=parameters, options=optionals)
806 |
807 |
808 | def get_order_reference_details(
809 | self,
810 | amazon_order_reference_id,
811 | access_token=None,
812 | address_consent_token=None,
813 | merchant_id=None,
814 | mws_auth_token=None):
815 | """Returns details about the order reference object and its current
816 | state.
817 |
818 | Parameters
819 | ----------
820 | amazon_order_reference_id : string, optional
821 | The order reference identifier. This value is retrieved from the
822 | amazon pay Button widget after the buyer has successfully authenticated
823 | with Amazon.
824 |
825 | access_token : string, optional
826 | The access token. This value is retrieved from the
827 | amazon pay Button widget after the buyer has successfully authenticated
828 | with Amazon. (Note: When using this value, you cannot use the
829 | address_consent_token at the same time, or this will cause an error.
830 | The same note applies when using just the address_consent_token)
831 |
832 | address_consent_token : string, optional
833 | The buyer address consent token. This value is retrieved from the
834 | amazon pay Button widget after the buyer has successfully authenticated
835 | with Amazon.
836 |
837 | merchant_id : string, required
838 | Your merchant ID. If you are a marketplace enter the seller's merchant
839 | ID.
840 |
841 | mws_auth_token: string, optional
842 | Your marketplace web service auth token. Default: None
843 | """
844 | parameters = {
845 | 'Action': 'GetOrderReferenceDetails',
846 | 'AmazonOrderReferenceId': amazon_order_reference_id
847 | }
848 | optionals = {
849 | 'AddressConsentToken': address_consent_token,
850 | 'AccessToken': access_token,
851 | 'SellerId': merchant_id,
852 | 'MWSAuthToken': mws_auth_token}
853 | return self._operation(params=parameters, options=optionals)
854 |
855 | def confirm_order_reference(
856 | self,
857 | amazon_order_reference_id,
858 | merchant_id=None,
859 | mws_auth_token=None,
860 | success_url=None,
861 | failure_url=None,
862 | authorization_amount=None,
863 | currency_code=None,
864 | expect_immediate_authorization=None):
865 | """Confirms that the order reference is free of constraints and all
866 | required information has been set on the order reference.
867 |
868 | Parameters
869 | ----------
870 | amazon_order_reference_id : string : required
871 | The order reference identifier.
872 |
873 | merchant_id : string, required
874 | Your merchant ID. If you are a marketplace enter the seller's merchant
875 | ID.
876 |
877 | mws_auth_token: string, optional
878 | Your marketplace web service auth token. Default: None
879 |
880 | success_url: string, optional
881 | Represents the return URL for SCA success.
882 |
883 | failure_url: string, optional
884 | Represents the return URL for SCA failure.
885 |
886 | authorization_amount: string, optional
887 | Represents the amount to be authorized. If blank both it and currency_code will be excluded.
888 |
889 | currency_code: string, optional
890 | Currency code for your region.
891 | Environment variable: AP_CURRENCY_CODE
892 |
893 | expect_immediate_authorization: boolean, optional
894 | If this value is set to true, the ORO will auto-close in case no authorize is triggered within 60 minutes after the confirm. Default: None
895 |
896 | """
897 | parameters = {
898 | 'Action': 'ConfirmOrderReference',
899 | 'AmazonOrderReferenceId': amazon_order_reference_id}
900 | optionals = {
901 | 'SellerId': merchant_id,
902 | 'MWSAuthToken': mws_auth_token,
903 | 'SuccessUrl': success_url,
904 | 'FailureUrl': failure_url,
905 | 'AuthorizationAmount.Amount': authorization_amount,
906 | 'AuthorizationAmount.CurrencyCode': self.currency_code if currency_code is None else currency_code,
907 | 'ExpectImmediateAuthorization': str(expect_immediate_authorization).lower() if expect_immediate_authorization is not None else None}
908 |
909 | if authorization_amount == "0" or authorization_amount is None:
910 | del optionals['AuthorizationAmount.Amount']
911 | del optionals['AuthorizationAmount.CurrencyCode']
912 |
913 | return self._operation(params=parameters, options=optionals)
914 |
915 | def cancel_order_reference(
916 | self,
917 | amazon_order_reference_id,
918 | cancelation_reason=None,
919 | merchant_id=None,
920 | mws_auth_token=None):
921 | """Cancels a previously confirmed order reference.
922 |
923 | Parameters
924 | ----------
925 | amazon_order_reference_id : string, required
926 | The order reference identifier.
927 |
928 | cancelation_reason : string, optional
929 | Describes the reason for the cancelation. Default: None
930 |
931 | merchant_id : string, required
932 | Your merchant ID. If you are a marketplace enter the seller's merchant
933 | ID.
934 |
935 | mws_auth_token: string, optional
936 | Your marketplace web service auth token. Default: None
937 | """
938 | parameters = {
939 | 'Action': 'CancelOrderReference',
940 | 'AmazonOrderReferenceId': amazon_order_reference_id}
941 | optionals = {
942 | 'CancelationReason': cancelation_reason,
943 | 'SellerId': merchant_id,
944 | 'MWSAuthToken': mws_auth_token}
945 | return self._operation(params=parameters, options=optionals)
946 |
947 | def close_order_reference(
948 | self,
949 | amazon_order_reference_id,
950 | closure_reason=None,
951 | merchant_id=None,
952 | mws_auth_token=None):
953 | """Confirms that an order reference has been fulfilled
954 | (fully or partially) and that you do not expect to create any new
955 | authorizations on this order reference.
956 |
957 | Parameters
958 | ----------
959 | amazon_order_reference_id : string, required
960 | The ID of the order reference for which the details are being
961 | requested.
962 |
963 | closure_reason : string, optional
964 | Describes the reason for closing the order reference. Default: None
965 |
966 | merchant_id : string, required
967 | Your merchant ID. If you are a marketplace enter the seller's merchant
968 | ID.
969 |
970 | mws_auth_token: string, optional
971 | Your marketplace web service auth token. Default: None
972 | """
973 | parameters = {
974 | 'Action': 'CloseOrderReference',
975 | 'AmazonOrderReferenceId': amazon_order_reference_id}
976 | optionals = {
977 | 'ClosureReason': closure_reason,
978 | 'SellerId': merchant_id,
979 | 'MWSAuthToken': mws_auth_token}
980 | return self._operation(params=parameters, options=optionals)
981 |
982 | def list_order_reference(
983 | self,
984 | query_id,
985 | query_id_type,
986 | created_time_range_start=None,
987 | created_time_range_end=None,
988 | sort_order=None,
989 | payment_domain=None,
990 | page_size=None,
991 | order_reference_status_list_filter=None,
992 | merchant_id=None,
993 | mws_auth_token=None):
994 |
995 | """
996 | Allows the search of any Amazon Pay order made using secondary
997 | seller order IDs generated manually, a solution provider, or a custom
998 | order fulfillment service.
999 |
1000 | Parameters
1001 | ==========================================
1002 | query_id: string, required
1003 | The identifier that the merchant wishes to use in relation to the
1004 | query id type.
1005 |
1006 | query_id_type: string, required
1007 | The type of query the id is referencing.
1008 | Note: At this time, you can only use the query type (SellerOrderId).
1009 | More options will be available in the future. Default: SellerOrderId
1010 |
1011 | payment_domain: string, optional
1012 | The region and currency that will be set to authorize and collect
1013 | payments from your customers. You can leave this blank for the
1014 | system to automatically assign the default payment domain for
1015 | your region.
1016 |
1017 | created_time_range_start: string, optional
1018 | This filter will allow a merchant to search for a particular item
1019 | within a date range of their choice.
1020 | Note: If you wish to use this filter, you MUST fill in an end date,
1021 | otherwise you will get an error returned when searching. You must
1022 | also use the ISO 8601 time format to return a valid response when
1023 | searching in a date range. Either of the two examples will work
1024 | when using this filter. Default: None
1025 | Example: YYYY-MM-DD or YYYY-MM-DDTHH:MM.
1026 |
1027 | created_time_range_end: string, optional
1028 | The end-date for the date range the merchant wishes to search for
1029 | any of their orders they're looking for.
1030 | Note: You need to only use this option if you are using the
1031 | created_time_range_start parameter. Default: None
1032 |
1033 | sort_order: string, optional
1034 | Filter can be set for "Ascending", or "Descending" order, and must
1035 | be written out as shown above. This will sort the orders via
1036 | the respective option. Default: None
1037 |
1038 | page_size: integer, optional
1039 | This filter limits how many results will be displayed per
1040 | request. Default: None
1041 |
1042 | merchant_id : string, optional
1043 | Your merchant ID. If you are a marketplace enter the seller's merchant
1044 | ID.
1045 |
1046 | mws_auth_token: string, optional
1047 | Your marketplace web service auth token. Default: None
1048 |
1049 | order_reference_status_list_filter: list (string), optional
1050 | When searching for an order, this filter is related to the status
1051 | of the orders on file. You can search for any valid status for orders
1052 | on file. Filters MUST be written out in English.
1053 | Example: "Open", "Closed", "Suspended", "Canceled"
1054 | Default: None
1055 | """
1056 |
1057 | if self.region is not None:
1058 | region_code = self.region.lower()
1059 | if region_code == 'na':
1060 | payment_domain = 'NA_USD'
1061 | elif region_code in ('uk', 'gb'):
1062 | payment_domain = 'EU_GBP'
1063 | elif region_code in ('jp', 'fe'):
1064 | payment_domain = 'FE_JPY'
1065 | elif region_code in ('eu', 'de', 'fr', 'it', 'es', 'cy'):
1066 | payment_domain = 'EU_EUR'
1067 | else:
1068 | raise ValueError("Error. The current region code does not match our records")
1069 |
1070 | parameters = {
1071 | 'Action': 'ListOrderReference',
1072 | 'QueryId': query_id,
1073 | 'QueryIdType': query_id_type,
1074 | 'PaymentDomain': payment_domain
1075 | }
1076 | optionals = {
1077 | 'CreatedTimeRange.StartTime': created_time_range_start,
1078 | 'CreatedTimeRange.EndTime': created_time_range_end,
1079 | 'SortOrder': sort_order,
1080 | 'SellerId': merchant_id,
1081 | 'MWSAuthToken': mws_auth_token,
1082 | 'PageSize': page_size
1083 | }
1084 |
1085 | if order_reference_status_list_filter is not None:
1086 | self._enumerate(
1087 | 'OrderReferenceStatusListFilter.OrderReferenceStatus.',
1088 | order_reference_status_list_filter, optionals)
1089 |
1090 | return self._operation(params=parameters, options=optionals)
1091 |
1092 | def list_order_reference_by_next_token(
1093 | self,
1094 | next_page_token,
1095 | merchant_id=None,
1096 | mws_auth_token=None):
1097 | """
1098 | next_page_token : string, required
1099 | Uses the key from a list_order_reference call that provides a
1100 | NextPageToken for a merchant to call to review the next page
1101 | of items, and if applicable, another NextPageToken for the next
1102 | set of items to read through.
1103 |
1104 | merchant_id : string, optional
1105 | Your merchant ID. If you are a marketplace enter the seller's merchant
1106 | ID.
1107 |
1108 | mws_auth_token: string, optional
1109 | Your marketplace web service auth token. Default: None
1110 |
1111 | """
1112 |
1113 | parameters = {
1114 | 'Action': 'ListOrderReferenceByNextToken',
1115 | 'NextPageToken': next_page_token}
1116 | optionals = {
1117 | 'SellerId': merchant_id,
1118 | 'MWSAuthToken': mws_auth_token}
1119 | return self._operation(params=parameters, options=optionals)
1120 |
1121 | def get_payment_details(
1122 | self,
1123 | amazon_order_reference_id,
1124 | merchant_id=None,
1125 | mws_auth_token=None):
1126 |
1127 | '''
1128 | This is a convenience function that will return every authorization,
1129 | charge, and refund call of an Amazon Pay order ID.
1130 |
1131 | Parameters
1132 | ----------
1133 | amazon_order_reference_id: string, required
1134 | The ID of the order reference for which the details are being
1135 | requested.
1136 |
1137 | merchant_id : string, optional
1138 | Your merchant ID. If you are a marketplace enter the seller's merchant
1139 | ID.
1140 |
1141 | mws_auth_token: string, optional
1142 | Your marketplace web service auth token. Default: None
1143 | '''
1144 |
1145 | parameters = {
1146 | 'Action': 'GetOrderReferenceDetails',
1147 | 'AmazonOrderReferenceId': amazon_order_reference_id
1148 | }
1149 |
1150 | optionals = {
1151 | 'SellerId': merchant_id,
1152 | 'MWSAuthToken': mws_auth_token
1153 | }
1154 |
1155 | query = self._operation(params=parameters, options=optionals)
1156 | answer = []
1157 | answer.append(query)
1158 | queryID = json.loads(query.to_json())
1159 | memberID = queryID['GetOrderReferenceDetailsResponse']\
1160 | ['GetOrderReferenceDetailsResult']['OrderReferenceDetails']['IdList']
1161 |
1162 | if memberID is not None:
1163 | '''
1164 | This check will see if the variable is in a list form or not
1165 | if it is not it will covert it into a single item list.
1166 | Otherwise, it will process the variable normally as a list.
1167 | '''
1168 | if type(memberID['member']) is not list:
1169 | memberID = [memberID['member']]
1170 | else:
1171 | memberID = memberID['member']
1172 |
1173 | for id in memberID:
1174 | parameters = {
1175 | 'Action': 'GetAuthorizationDetails',
1176 | 'AmazonAuthorizationId': id
1177 | }
1178 | response = self._operation(params=parameters)
1179 | answer.append(response)
1180 | queryID = json.loads(response.to_json())
1181 | chargeID = queryID['GetAuthorizationDetailsResponse']\
1182 | ['GetAuthorizationDetailsResult']['AuthorizationDetails']['IdList']
1183 |
1184 | if chargeID is not None:
1185 | chargeID = chargeID['member']
1186 | parameters = {
1187 | 'Action': 'GetCaptureDetails',
1188 | 'AmazonCaptureId': chargeID
1189 | }
1190 | response = self._operation(params=parameters)
1191 | queryID = json.loads(response.to_json())
1192 | refundID = queryID['GetCaptureDetailsResponse']\
1193 | ['GetCaptureDetailsResult']['CaptureDetails']['IdList']
1194 | answer.append(response)
1195 |
1196 | if refundID is not None:
1197 | if type(refundID['member']) is not list:
1198 | refundID = [refundID['member']]
1199 | else:
1200 | refundID = refundID['member']
1201 |
1202 | for id in refundID:
1203 | parameters = {
1204 | 'Action': 'GetRefundDetails',
1205 | 'AmazonRefundId': id
1206 | }
1207 | response = self._operation(params=parameters)
1208 | answer.append(response)
1209 |
1210 | return answer
1211 |
1212 | def authorize(
1213 | self,
1214 | amazon_order_reference_id,
1215 | authorization_reference_id,
1216 | authorization_amount,
1217 | seller_authorization_note=None,
1218 | transaction_timeout=1440,
1219 | capture_now=False,
1220 | soft_descriptor=None,
1221 | merchant_id=None,
1222 | mws_auth_token=None):
1223 | # pylint: disable=too-many-arguments
1224 | """Reserves a specified amount against the payment method(s) stored in
1225 | the order reference.
1226 |
1227 | Parameters
1228 | ----------
1229 | amazon_order_reference_id : string, required
1230 | The order reference identifier.
1231 |
1232 | authorization_reference_id : string, required
1233 | The identifier for this authorization transaction that you specify.
1234 | This identifier must be unique for all your transactions
1235 | (authorization, capture, refund, etc.).
1236 |
1237 | authorization_amount : string, required
1238 | Represents the amount to be authorized.
1239 |
1240 | seller_authorization_note : string, optional
1241 | A description for the transaction that is displayed in emails to
1242 | the buyer. Maximum: 225 characters.
1243 |
1244 | transaction_timeout : unsigned integer, optional
1245 | The number of minutes after which the authorization will
1246 | automatically be closed and you will not be able to capture funds
1247 | against the authorization.
1248 |
1249 | Note: In asynchronous mode, the Authorize operation always returns
1250 | the State as Pending. The authorization remains in this state until
1251 | it is processed by Amazon. The processing time varies and can be a
1252 | minute or more. After processing is complete, Amazon will notify
1253 | you of the final processing status. For more information, see
1254 | Synchronizing your systems with Amazon Pay in the Amazon Pay
1255 | Integration Guide. Default: 1440
1256 |
1257 | capture_now : boolean, optional
1258 | Indicates whether to directly capture a specified amount against an
1259 | order reference (without needing to call Capture and without waiting
1260 | until the order ships). The captured amount is disbursed to your
1261 | account in the next disbursement cycle.
1262 |
1263 | Note: The Amazon Pay policy states that you charge your buyer
1264 | when you fulfill the items in the order. You should not collect
1265 | funds prior to fulfilling the order. Default: False
1266 |
1267 | soft_descriptor : string, optional
1268 | The description to be shown on the buyer’s payment instrument
1269 | statement if CaptureNow is set to true. The soft descriptor sent to
1270 | the payment processor is: “AMZ* ”.
1271 |
1272 | merchant_id : string, required
1273 | Your merchant ID. If you are a marketplace enter the seller's merchant
1274 | ID.
1275 |
1276 | mws_auth_token: string, optional
1277 | Your marketplace web service auth token. Default: None
1278 | """
1279 | parameters = {
1280 | 'Action': 'Authorize',
1281 | 'AmazonOrderReferenceId': amazon_order_reference_id,
1282 | 'TransactionTimeout': transaction_timeout,
1283 | 'AuthorizationReferenceId': authorization_reference_id,
1284 | 'AuthorizationAmount.Amount': authorization_amount,
1285 | 'AuthorizationAmount.CurrencyCode': self.currency_code}
1286 | optionals = {
1287 | 'SellerAuthorizationNote': seller_authorization_note,
1288 | 'CaptureNow': str(capture_now).lower(),
1289 | 'SoftDescriptor': soft_descriptor,
1290 | 'SellerId': merchant_id,
1291 | 'MWSAuthToken': mws_auth_token}
1292 | return self._operation(params=parameters, options=optionals)
1293 |
1294 | def get_authorization_details(
1295 | self,
1296 | amazon_authorization_id,
1297 | merchant_id=None,
1298 | mws_auth_token=None):
1299 | """Returns the status of a particular authorization and the total amount
1300 | captured on the authorization.
1301 |
1302 | Parameters
1303 | ----------
1304 | amazon_authorization_id : string, required
1305 | The authorization identifier that was generated by Amazon in the
1306 | earlier call to Authorize.
1307 |
1308 | merchant_id : string, required
1309 | Your merchant ID. If you are a marketplace enter the seller's merchant
1310 | ID.
1311 |
1312 | mws_auth_token: string, optional
1313 | Your marketplace web service auth token. Default: None
1314 | """
1315 | parameters = {
1316 | 'Action': 'GetAuthorizationDetails',
1317 | 'AmazonAuthorizationId': amazon_authorization_id}
1318 | optionals = {
1319 | 'SellerId': merchant_id,
1320 | 'MWSAuthToken': mws_auth_token}
1321 | return self._operation(params=parameters, options=optionals)
1322 |
1323 | def capture(
1324 | self,
1325 | amazon_authorization_id,
1326 | capture_reference_id,
1327 | capture_amount,
1328 | seller_capture_note=None,
1329 | soft_descriptor=None,
1330 | merchant_id=None,
1331 | mws_auth_token=None):
1332 | # pylint: disable=too-many-arguments
1333 | """Captures funds from an authorized payment instrument.
1334 |
1335 | Parameters
1336 | ----------
1337 | amazon_authorization_id : string, required
1338 | The authorization identifier that was generated by Amazon in the
1339 | earlier call to Authorize or AuthorizeOnBillingAgreement.
1340 |
1341 | capture_reference_id : string, required
1342 | The identifier for this capture transaction that you specify. This
1343 | identifier must be unique for all your transactions
1344 | (authorization, capture, refund, etc.).
1345 |
1346 | capture_amount : string, required
1347 | The amount to capture in this transaction. This amount cannot exceed
1348 | the original amount that was authorized less any previously captured
1349 | amount on this authorization.
1350 |
1351 | seller_capture_note : string, optional
1352 | A description for the capture transaction that is displayed in
1353 | emails to the buyer. Maximum: 255 characters, Default: None
1354 |
1355 | soft_descriptor : string, optional
1356 | The description to be shown on the buyer’s payment instrument
1357 | statement. The soft descriptor sent to the payment processor is:
1358 | “AMZ* ”.
1359 |
1360 | merchant_id : string, required
1361 | Your merchant ID. If you are a marketplace enter the seller's merchant
1362 | ID.
1363 |
1364 | mws_auth_token: string, optional
1365 | Your marketplace web service auth token. Default: None
1366 | """
1367 | parameters = {
1368 | 'Action': 'Capture',
1369 | 'AmazonAuthorizationId': amazon_authorization_id,
1370 | 'CaptureReferenceId': capture_reference_id,
1371 | 'CaptureAmount.Amount': capture_amount,
1372 | 'CaptureAmount.CurrencyCode': self.currency_code}
1373 | optionals = {
1374 | 'SellerCaptureNote': seller_capture_note,
1375 | 'SoftDescriptor': soft_descriptor,
1376 | 'SellerId': merchant_id,
1377 | 'MWSAuthToken': mws_auth_token}
1378 | return self._operation(params=parameters, options=optionals)
1379 |
1380 | def get_capture_details(
1381 | self,
1382 | amazon_capture_id,
1383 | merchant_id=None,
1384 | mws_auth_token=None):
1385 | """Returns the status of a particular capture and the total amount
1386 | refunded on the capture.
1387 |
1388 | Parameters
1389 | ----------
1390 | amazon_capture_id : string, required
1391 | The capture identifier that was generated by Amazon on the earlier
1392 | call to Capture.
1393 |
1394 | merchant_id : string, required
1395 | Your merchant ID. If you are a marketplace enter the seller's merchant
1396 | ID.
1397 |
1398 | mws_auth_token: string, optional
1399 | Your marketplace web service auth token. Default: None
1400 | """
1401 | parameters = {
1402 | 'Action': 'GetCaptureDetails',
1403 | 'AmazonCaptureId': amazon_capture_id}
1404 | optionals = {
1405 | 'SellerId': merchant_id,
1406 | 'MWSAuthToken': mws_auth_token}
1407 | return self._operation(params=parameters, options=optionals)
1408 |
1409 | def close_authorization(
1410 | self,
1411 | amazon_authorization_id,
1412 | closure_reason=None,
1413 | merchant_id=None,
1414 | mws_auth_token=None):
1415 | """Closes an authorization.
1416 |
1417 | Parameters
1418 | ----------
1419 | amazon_authorization_id : string, required
1420 | The authorization identifier that was generated by Amazon in the
1421 | earlier call to Authorize.
1422 |
1423 | closure_reason : string, optional
1424 | A description for the closure that is displayed in emails to the
1425 | buyer.
1426 |
1427 | merchant_id : string, required
1428 | Your merchant ID. If you are a marketplace enter the seller's merchant
1429 | ID.
1430 |
1431 | mws_auth_token: string, optional
1432 | Your marketplace web service auth token. Default: None
1433 | """
1434 | parameters = {
1435 | 'Action': 'CloseAuthorization',
1436 | 'AmazonAuthorizationId': amazon_authorization_id}
1437 | optionals = {
1438 | 'ClosureReason': closure_reason,
1439 | 'SellerId': merchant_id,
1440 | 'MWSAuthToken': mws_auth_token}
1441 | return self._operation(params=parameters, options=optionals)
1442 |
1443 | def refund(
1444 | self,
1445 | amazon_capture_id,
1446 | refund_reference_id,
1447 | refund_amount,
1448 | seller_refund_note=None,
1449 | soft_descriptor=None,
1450 | merchant_id=None,
1451 | mws_auth_token=None):
1452 | # pylint: disable=too-many-arguments
1453 | """Refunds a previously captured amount.
1454 |
1455 | Parameters
1456 | ----------
1457 | amazon_capture_id : string, required
1458 | The capture identifier that was generated by Amazon in the earlier
1459 | call to Capture.
1460 |
1461 | refund_reference_id : string, required
1462 | The identifier for this refund transaction that you specify. This
1463 | identifier must be unique for all your transactions
1464 | (authorization, capture, refund, etc.).
1465 |
1466 | refund_amount : string, required
1467 | The amount to refund. This amount cannot exceed:
1468 | In the US: the lesser of 15% or $75 above the captured amount
1469 | less the amount already refunded on the capture.
1470 | In the UK: the lesser of 15% or £75 above the captured amount
1471 | for the Capture object.
1472 | In Germany: the lesser of 15% or €75 above the captured amount
1473 | for the Capture object.
1474 |
1475 | seller_refund_note : string, optional
1476 | A description for the refund that is displayed in emails to the
1477 | buyer. Maximum: 255 characters, Default: None
1478 |
1479 | soft_descriptor : string, optional
1480 | The description to be shown on the buyer’s payment instrument
1481 | statement. The soft descriptor sent to the payment processor is:
1482 | “AMZ* ”.
1483 |
1484 | merchant_id : string, required
1485 | Your merchant ID. If you are a marketplace enter the seller's merchant
1486 | ID.
1487 |
1488 | mws_auth_token: string, optional
1489 | Your marketplace web service auth token. Default: None
1490 | """
1491 | parameters = {
1492 | 'Action': 'Refund',
1493 | 'AmazonCaptureId': amazon_capture_id,
1494 | 'RefundReferenceId': refund_reference_id,
1495 | 'RefundAmount.Amount': refund_amount,
1496 | 'RefundAmount.CurrencyCode': self.currency_code}
1497 | optionals = {
1498 | 'SellerRefundNote': seller_refund_note,
1499 | 'SoftDescriptor': soft_descriptor,
1500 | 'SellerId': merchant_id,
1501 | 'MWSAuthToken': mws_auth_token}
1502 | return self._operation(params=parameters, options=optionals)
1503 |
1504 | def get_refund_details(
1505 | self,
1506 | amazon_refund_id,
1507 | merchant_id=None,
1508 | mws_auth_token=None):
1509 | """Returns the status of a particular refund.
1510 |
1511 | Parameters
1512 | ----------
1513 | amazon_refund_id : string, required
1514 | The Amazon-generated identifier for this refund transaction.
1515 |
1516 | merchant_id : string, required
1517 | Your merchant ID. If you are a marketplace enter the seller's merchant
1518 | ID.
1519 |
1520 | mws_auth_token: string, optional
1521 | Your marketplace web service auth token. Default: None
1522 | """
1523 | parameters = {
1524 | 'Action': 'GetRefundDetails',
1525 | 'AmazonRefundId': amazon_refund_id}
1526 | optionals = {
1527 | 'SellerId': merchant_id,
1528 | 'MWSAuthToken': mws_auth_token}
1529 | return self._operation(params=parameters, options=optionals)
1530 |
1531 | def get_service_status(self):
1532 | """Returns the operational status of the Off-Amazon Payments API section.
1533 | """
1534 | parameters = {
1535 | 'Action': 'GetServiceStatus'}
1536 |
1537 | return self._operation(params=parameters)
1538 |
1539 | def charge(
1540 | self,
1541 | amazon_reference_id,
1542 | charge_amount,
1543 | authorize_reference_id,
1544 | charge_note,
1545 | charge_order_id=None,
1546 | store_name=None,
1547 | custom_information=None,
1548 | platform_id=None,
1549 | merchant_id=None,
1550 | mws_auth_token=None,
1551 | soft_descriptor=None):
1552 | """Combine the set, confirm, authorize, and capture calls into one.
1553 |
1554 | Parameters
1555 | ----------
1556 | amazon_reference_id : string, required
1557 | The order reference or billing agreement identifier.
1558 |
1559 | charge_amount : string, required
1560 | The amount to capture in this transaction.
1561 |
1562 | authorize_reference_id : string, required
1563 | The seller-specified identifier of this charge. This parameter sets
1564 | both authorization_reference_id and capture_reference_id.
1565 |
1566 | charge_order_id : string, optional
1567 | The seller-specified identifier of this order. This is displayed to
1568 | the buyer in the emails they receive from Amazon and also in their
1569 | transaction history on the Amazon Pay website.
1570 |
1571 | store_name : string, optional
1572 | The identifier of the store from which the order was placed. This
1573 | overrides the default value in Seller Central under Settings >
1574 | Account Settings. It is displayed to the buyer in the email they
1575 | receive from Amazon and also in their transaction history on the
1576 | Amazon Pay website.
1577 |
1578 | custom_information : string, optional
1579 | Any additional information you wish to include with this billing
1580 | agreement.
1581 |
1582 | charge_note : string, optional
1583 | A description for the capture transaction that is displayed in
1584 | emails to the buyer.
1585 |
1586 | platform_id
1587 | Represents the SellerId of the Solution Provider that developed the
1588 | platform. This value should only be provided by Solution Providers.
1589 | It should not be provided by sellers creating their own custom
1590 | integration.
1591 |
1592 | merchant_id : string, required
1593 | Your merchant ID. If you are a marketplace enter the seller's merchant
1594 | ID.
1595 |
1596 | mws_auth_token: string, optional
1597 | Your marketplace web service auth token. Default: None
1598 |
1599 | soft_descriptor : string, optional
1600 | The description to be shown on the buyer’s payment instrument
1601 | statement if CaptureNow is set to true. The soft descriptor sent to
1602 | the payment processor is: “AMZ* ”.
1603 | """
1604 |
1605 | if self.is_order_reference_id(amazon_reference_id):
1606 | # set
1607 | ret = self.set_order_reference_details(
1608 | amazon_order_reference_id=amazon_reference_id,
1609 | order_total=charge_amount,
1610 | platform_id=platform_id,
1611 | seller_note=charge_note,
1612 | seller_order_id=charge_order_id,
1613 | store_name=store_name,
1614 | custom_information=custom_information,
1615 | merchant_id=merchant_id,
1616 | mws_auth_token=mws_auth_token)
1617 | if ret.success:
1618 | # confirm
1619 | ret = self.confirm_order_reference(
1620 | amazon_order_reference_id=amazon_reference_id,
1621 | merchant_id=merchant_id,
1622 | mws_auth_token=mws_auth_token)
1623 | if ret.success:
1624 | # auth
1625 | ret = self.authorize(
1626 | amazon_order_reference_id=amazon_reference_id,
1627 | authorization_reference_id=authorize_reference_id,
1628 | authorization_amount=charge_amount,
1629 | seller_authorization_note=charge_note,
1630 | transaction_timeout=0,
1631 | capture_now=True,
1632 | soft_descriptor=soft_descriptor,
1633 | merchant_id=merchant_id,
1634 | mws_auth_token=mws_auth_token)
1635 | return ret
1636 | else:
1637 | return ret
1638 | else:
1639 | return ret
1640 |
1641 | if self.is_billing_agreement_id(amazon_reference_id):
1642 | """Since this is a billing agreement we need to see if details have
1643 | already been set. If so, we just need to authorize.
1644 | """
1645 | ret = self.get_billing_agreement_details(
1646 | amazon_billing_agreement_id=amazon_reference_id,
1647 | address_consent_token=None,
1648 | merchant_id=merchant_id,
1649 | mws_auth_token=mws_auth_token)
1650 | if ret.to_dict().get('GetBillingAgreementDetailsResponse').get(
1651 | 'GetBillingAgreementDetailsResult').get(
1652 | 'BillingAgreementDetails').get(
1653 | 'BillingAgreementStatus').get('State') == 'Draft':
1654 | # set
1655 | ret = self.set_billing_agreement_details(
1656 | amazon_billing_agreement_id=amazon_reference_id,
1657 | platform_id=platform_id,
1658 | seller_note=charge_note,
1659 | seller_billing_agreement_id=charge_order_id,
1660 | store_name=store_name,
1661 | custom_information=custom_information,
1662 | merchant_id=merchant_id,
1663 | mws_auth_token=mws_auth_token)
1664 | if ret.success:
1665 | # confirm
1666 | ret = self.confirm_billing_agreement(
1667 | amazon_billing_agreement_id=amazon_reference_id,
1668 | merchant_id=merchant_id,
1669 | mws_auth_token=mws_auth_token)
1670 | if not ret.success:
1671 | return ret
1672 | else:
1673 | return ret
1674 | # auth
1675 | ret = self.authorize_on_billing_agreement(
1676 | amazon_billing_agreement_id=amazon_reference_id,
1677 | authorization_reference_id=authorize_reference_id,
1678 | authorization_amount=charge_amount,
1679 | seller_authorization_note=charge_note,
1680 | transaction_timeout=0,
1681 | capture_now=True,
1682 | soft_descriptor=soft_descriptor,
1683 | seller_note=charge_note,
1684 | platform_id=platform_id,
1685 | seller_order_id=charge_order_id,
1686 | store_name=store_name,
1687 | custom_information=custom_information,
1688 | inherit_shipping_address=True,
1689 | merchant_id=merchant_id,
1690 | mws_auth_token=mws_auth_token)
1691 | return ret
1692 |
1693 | def is_order_reference_id(self, amazon_reference_id):
1694 | """Checks if Id is order reference. P or S at the beginning indicate a
1695 | order reference ID.
1696 | """
1697 | return re.search('^(P|S)', amazon_reference_id)
1698 |
1699 | def is_billing_agreement_id(self, amazon_reference_id):
1700 | """Checks if Id is billing agreement. B or C at the beginning indicate a
1701 | billing agreement ID
1702 | """
1703 | return re.search('^(B|C)', amazon_reference_id)
1704 |
1705 | def _operation(self, params, options=None):
1706 | """Parses required and optional parameters and passes to the Request
1707 | object.
1708 | """
1709 |
1710 | if options is not None:
1711 | for opt in options.keys():
1712 | if options[opt] is not None:
1713 | params[opt] = options[opt]
1714 |
1715 | request = PaymentRequest(
1716 | params=params,
1717 | config={'mws_access_key': self.mws_access_key,
1718 | 'mws_secret_key': self.mws_secret_key,
1719 | 'api_version': self._api_version,
1720 | 'merchant_id': self.merchant_id,
1721 | 'mws_endpoint': self._mws_endpoint,
1722 | 'headers': self._headers,
1723 | 'handle_throttle': self.handle_throttle})
1724 |
1725 | request.send_post()
1726 | return request.response
1727 |
1728 | def _enumerate(
1729 | self,
1730 | category,
1731 | filter_types,
1732 | optionals):
1733 |
1734 | def enumerate_param(param, values):
1735 | """
1736 | Builds a dictionary of an enumerated parameter from the filter list.
1737 | This is currently used for two API calls in the client. Specifically,
1738 | set_order_attributes & list_order_reference
1739 | Example: (For set_order_attributes)
1740 | enumerate_param(
1741 | 'OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.',
1742 | (["Antiques", "Outdoor"])
1743 | returns
1744 | {
1745 | OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.1:
1746 | Antiques,
1747 | OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.2:
1748 | Outdoor
1749 | }
1750 | """
1751 | params = {}
1752 | if values is not None:
1753 | if not param.endswith('.'):
1754 | param = "%s." % param
1755 | for num, value in enumerate(values):
1756 | params['%s%d' % (param, (num + 1))] = value
1757 | return params
1758 |
1759 | """This will apply your filters from the list filter parameter"""
1760 | if isinstance(filter_types, list):
1761 | optionals.update(enumerate_param(category, filter_types))
1762 | else:
1763 | if ',' in filter_types:
1764 | filter_types = filter_types.replace(' ','')
1765 | filter_types = filter_types.split(',')
1766 | optionals.update(enumerate_param(category, filter_types))
1767 | else:
1768 | raise Exception("Invalid format for this request.")
1769 |
--------------------------------------------------------------------------------
/amazon_pay/ipn_handler.py:
--------------------------------------------------------------------------------
1 | import re
2 | import json
3 | import base64
4 | import logging
5 | from urllib import request
6 | from OpenSSL import crypto
7 | from urllib.error import HTTPError
8 | from urllib.parse import urlparse
9 | from amazon_pay.payment_response import PaymentResponse
10 |
11 |
12 | class IpnHandler():
13 |
14 | logger = logging.getLogger('__amazon_pay_sdk__')
15 | logger.addHandler(logging.NullHandler())
16 |
17 | """Instant Payment Notifications (IPN) can be used to monitor the state
18 | transition of payment objects.
19 |
20 | Amazon sends you a notification when the state of any of the payment
21 | objects or the Order Reference object changes. These notifications are
22 | always sent without any action required on your part and can be used to
23 | update any internal tracking or fulfillment systems you might be using to
24 | manage the order.
25 |
26 | After you receive an IPN, a best practice is to perform a get operation for
27 | the respective object for which you have received the notification. You can
28 | use the response of the get operation to update your systems.
29 |
30 | With each notification you receive, you should configure your endpoint to
31 | send Amazon a '200 OK' response immediately after receipt. If you do not
32 | send this response or if your server is down when the SNS message is sent,
33 | Amazon SNS will perform retries every hour for 14 days.
34 |
35 | Amazon Simple Notification Service (Amazon SNS) is a fast, flexible, fully
36 | managed push notification service.
37 | """
38 |
39 | def __init__(self, body, headers):
40 | """
41 | Parameters
42 | ----------
43 | body : string
44 | The body of the SNS message.
45 |
46 | headers : dictionary
47 | The headers of the SNS message.
48 |
49 |
50 | Properties
51 | ----------
52 | error : string
53 | Holds the latest error, if any.
54 | """
55 |
56 | self.error = None
57 |
58 | self._root = None
59 | self._ns = None
60 | self._response_type = None
61 | self._headers = headers
62 | self._payload = json.loads(body.decode('utf-8'))
63 | self._pem = None
64 |
65 | self._message_encoded = self._payload['Message']
66 | self._message = json.loads(self._payload['Message'])
67 | self._message_id = self._payload['MessageId']
68 | self._topic_arn = self._payload['TopicArn']
69 | self._notification_data = self._message['NotificationData']
70 | self._signing_cert_url = self._payload['SigningCertURL']
71 | self._signature = self._payload['Signature']
72 | self._timestamp = self._payload['Timestamp']
73 | self._type = self._payload['Type']
74 | self._xml = self._notification_data.replace(
75 | '\n',
76 | '')
77 | self.logger.debug('IPN Response: %s',
78 | self._sanitize_response_data(self._xml))
79 |
80 | def authenticate(self):
81 | """Attempt to validate a SNS message received from Amazon
82 | From release version 2.7.9/3.4.3 on, Python by default attempts to
83 | perform certificate validation. Returns True on success.
84 |
85 | https://docs.python.org/2/library/httplib.html#httplib.HTTPSConnection
86 |
87 | Changed in version 3.4.3: This class now performs all the necessary
88 | certificate and hostname checks by default.
89 | """
90 | self._validate_header()
91 | self._validate_cert_url()
92 | self._get_cert()
93 | self._validate_signature()
94 |
95 | return True
96 |
97 | def _validate_header(self):
98 | """Compare the header topic_arn to the body topic_arn """
99 | if 'X-Amz-Sns-Topic-Arn' in self._headers:
100 | if self._topic_arn != self._headers.get(
101 | 'X-Amz-Sns-Topic-Arn'):
102 | self.error = 'Invalid TopicArn.'
103 | raise ValueError('Invalid TopicArn')
104 | else:
105 | self.error = 'Invalid TopicArn'
106 | raise ValueError('Invalid TopicArn')
107 |
108 | return True
109 |
110 | def _validate_cert_url(self):
111 | """Checks to see if the certificate URL points to a AWS endpoint and
112 | validates the signature using the .pem from the certificate URL.
113 | """
114 | try:
115 | url_object = urlparse(self._signing_cert_url)
116 | except:
117 | raise ValueError('Invalid signing cert URL.')
118 |
119 | if url_object.scheme != 'https':
120 | raise ValueError('Invalid certificate.')
121 |
122 | if not re.search(
123 | '^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$', url_object.netloc):
124 | raise ValueError('Invalid certificate.')
125 |
126 | if not re.search('^\/(.*)\.pem$', url_object.path):
127 | raise ValueError('Invalid certificate.')
128 |
129 | return True
130 |
131 | def _get_cert(self):
132 | try:
133 | cert_req = request.urlopen(
134 | url=request.Request(self._signing_cert_url))
135 | except HTTPError as ex:
136 | self.error = 'Error retrieving certificate.'
137 | raise ValueError(
138 | 'Error retrieving certificate. {}'.format(
139 | ex.reason))
140 |
141 | self._pem = str(cert_req.read(), encoding='utf-8')
142 | return True
143 |
144 | def _validate_signature(self):
145 | """Generate signing string and validate signature"""
146 | signing_string = '{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n'.format(
147 | 'Message',
148 | self._message_encoded,
149 | 'MessageId',
150 | self._message_id,
151 | 'Timestamp',
152 | self._timestamp,
153 | 'TopicArn',
154 | self._topic_arn,
155 | 'Type',
156 | self._type)
157 |
158 | crt = crypto.load_certificate(crypto.FILETYPE_PEM, self._pem)
159 | signature = base64.b64decode(self._signature)
160 |
161 | try:
162 | crypto.verify(
163 | crt,
164 | signature,
165 | signing_string.encode('utf-8'),
166 | 'sha1')
167 | except:
168 | self.error = 'Invalid signature.'
169 | raise ValueError('Invalid signature.')
170 |
171 | return True
172 |
173 | def to_json(self):
174 | """Retuns notification message as JSON"""
175 | return PaymentResponse(self._xml).to_json()
176 |
177 | def to_xml(self):
178 | """Retuns notification message as XML"""
179 | return PaymentResponse(self._xml).to_xml()
180 |
181 | def _sanitize_response_data(self, text):
182 | editText = text
183 | patterns = []
184 | patterns.append(r'(?s)().*(<\/SellerNote>)')
185 | patterns.append(r'(?s)().*(<\/AuthorizationBillingAddress>)')
186 | patterns.append(r'(?s)().*(<\/SellerAuthorizationNote>)')
187 | patterns.append(r'(?s)().*(<\/SellerCaptureNote>)')
188 | patterns.append(r'(?s)().*(<\/SellerRefundNote>)')
189 | replacement = r'\1 REMOVED \2'
190 |
191 | for pattern in patterns:
192 | editText = re.sub(pattern, replacement, editText)
193 | return editText
--------------------------------------------------------------------------------
/amazon_pay/login_with_amazon.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import amazon_pay.lwa_region as lwa_region
3 |
4 |
5 | class LoginWithAmazon:
6 |
7 | """Login with Amazon class to wrap the get login profile method"""
8 |
9 | def __init__(self, client_id, region, sandbox=False):
10 | """
11 | Parameters
12 | ----------
13 | client_id: string, required
14 | The client Id of your Login with Amazon application.
15 |
16 | region : string, required
17 | The region in which you are conducting business.
18 |
19 | sandbox : string, optional
20 | Toggle sandbox mode. Default: False
21 | """
22 | self._client_id = client_id
23 |
24 | try:
25 | self.region = lwa_region.regions[region]
26 | except KeyError:
27 | raise KeyError('Invalid region code ({}).'.format(region))
28 |
29 | self._sandbox_str = 'api.sandbox' if sandbox else 'api'
30 | self._endpoint = 'https://{}.{}'.format(
31 | self._sandbox_str,
32 | self.region)
33 |
34 | def get_login_profile(self, access_token):
35 | """Get profile associated with LWA user."""
36 | token_info = requests.get(
37 | url='{}/auth/o2/tokeninfo'.format(self._endpoint),
38 | headers={'x-amz-access-token': access_token},
39 | params=None,
40 | verify=True)
41 |
42 | token_decoded = token_info.json()
43 |
44 | if 'error' in token_decoded:
45 | raise ValueError(token_decoded['error'])
46 |
47 | if 'aud' not in token_decoded:
48 | raise ValueError('Client Id not present.')
49 |
50 | if token_decoded['aud'] != self._client_id:
51 | raise ValueError('Invalid client Id.')
52 |
53 | profile = requests.get(
54 | url='{}/user/profile'.format(self._endpoint),
55 | headers={'x-amz-access-token': access_token},
56 | params=None,
57 | verify=True)
58 |
59 | return profile.json()
60 |
--------------------------------------------------------------------------------
/amazon_pay/lwa_region.py:
--------------------------------------------------------------------------------
1 | regions = {'jp': 'amazon.co.jp',
2 | 'uk': 'amazon.co.uk',
3 | 'de': 'amazon.de',
4 | 'eu': 'amazon.co.uk',
5 | 'us': 'amazon.com',
6 | 'na': 'amazon.com'}
7 |
--------------------------------------------------------------------------------
/amazon_pay/payment_request.py:
--------------------------------------------------------------------------------
1 | import hmac
2 | import time
3 | import base64
4 | import hashlib
5 | import datetime
6 | import requests
7 | import logging
8 | import re
9 | from urllib import parse
10 | from collections import OrderedDict
11 | from amazon_pay.payment_response import PaymentResponse, PaymentErrorResponse
12 |
13 |
14 | class PaymentRequest:
15 |
16 | logger = logging.getLogger('__amazon_pay_sdk__')
17 | logger.addHandler(logging.NullHandler())
18 |
19 | """Parses request, generates signature and parameter string, posts
20 | request to Amazon, and returns result.
21 | """
22 |
23 | def __init__(self, params, config):
24 | """
25 | Parameters
26 | ----------
27 | params : dictionary, required
28 | Dictionary containing keys passed from the _operation method. Each
29 | API call fills this dictionary so you shouldn't need to modify this.
30 | The keys will vary depending on the API call.
31 |
32 | config : dictionary, required
33 | Dictionary containing configuration information.
34 | Required keys: mws_access_key, mws_secret_key, api_version,
35 | merchant_id, mws_endpoint, headers, handle_throttle
36 | """
37 | self.success = False
38 | self.response = None
39 | self.mws_access_key = config['mws_access_key']
40 | self.mws_secret_key = config['mws_secret_key']
41 | self.merchant_id = config['merchant_id']
42 | self.handle_throttle = config['handle_throttle']
43 |
44 | self._retry_time = 0
45 | self._params = params
46 | self._api_version = config['api_version']
47 | self._mws_endpoint = config['mws_endpoint']
48 | self._headers = config['headers']
49 | self._should_throttle = False
50 |
51 | def _sign(self, string_to_sign):
52 | """Generate the signature for the request"""
53 | signature = hmac.new(
54 | self.mws_secret_key.encode('utf_8'),
55 | msg=string_to_sign.encode('utf_8'),
56 | digestmod=hashlib.sha256).digest()
57 | signature = base64.b64encode(signature).decode()
58 | self.logger.debug('string to generate signature: %s', string_to_sign)
59 | self.logger.debug('signature: %s', signature)
60 | return signature
61 |
62 | def _querystring(self, params):
63 | """Generate the querystring to be posted to the MWS endpoint
64 |
65 | Required parameters for every API call.
66 |
67 | AWSAccessKeyId: Your Amazon MWS account is identified by your access key,
68 | which Amazon MWS uses to look up your secret key.
69 |
70 | SignatureMethod: The HMAC hash algorithm you are using to calculate your
71 | signature. Both HmacSHA256 and HmacSHA1 are supported hash algorithms,
72 | but Amazon recommends using HmacSHA256.
73 |
74 | SignatureVersion: Which signature version is being used. This is Amazon
75 | MWS-specific information that tells Amazon MWS the algorithm you used
76 | to form the string that is the basis of the signature. For Amazon MWS,
77 | this value is currently SignatureVersion=2.
78 |
79 | Version: The version of the API section being called.
80 |
81 | Timestamp: Each request must contain the timestamp of the request. The
82 | Timestamp attribute must contain the client's machine time in
83 | ISO8601 format; requests with a timestamp significantly different
84 | (15 minutes) than the receiving machine's clock will be rejected to
85 | help prevent replay attacks.
86 |
87 | SellerId: Your seller or merchant identifier.
88 | """
89 | parameters = {'AWSAccessKeyId': self.mws_access_key,
90 | 'SignatureMethod': 'HmacSHA256',
91 | 'SignatureVersion': '2',
92 | 'Version': self._api_version,
93 | 'Timestamp': datetime.datetime.utcnow().replace(
94 | microsecond=0).isoformat(sep='T') + 'Z'}
95 |
96 | if 'SellerId' not in params:
97 | parameters['SellerId'] = self.merchant_id
98 |
99 | parameters.update({k: v for (k, v) in params.items()})
100 | parse_results = parse.urlparse(self._mws_endpoint)
101 |
102 | string_to_sign = "POST\n{}\n{}\n{}".format(
103 | parse_results[1],
104 | parse_results[2],
105 | parse.urlencode(
106 | sorted(parameters.items())).replace(
107 | '+', '%20').replace('*', '%2A').replace('%7E', '~'))
108 |
109 | parameters['Signature'] = self._sign(string_to_sign)
110 |
111 | ordered_parameters = OrderedDict(sorted(parameters.items()))
112 | ordered_parameters.move_to_end('Signature')
113 | return parse.urlencode(ordered_parameters).encode(encoding='utf_8')
114 |
115 | def _request(self, retry_time):
116 | time.sleep(retry_time)
117 | data = self._querystring(self._params)
118 |
119 | self.logger.debug('Request Header: %s',
120 | self._sanitize_request_data(str(self._headers)))
121 |
122 | r = requests.post(
123 | url=self._mws_endpoint,
124 | data=data,
125 | headers=self._headers,
126 | verify=True)
127 | r.encoding = 'utf-8'
128 | self._status_code = r.status_code
129 |
130 | if self._status_code == 200:
131 | self.success = True
132 | self._should_throttle = False
133 | self.response = PaymentResponse(r.text)
134 | self.logger.debug('Response: %s',
135 | self._sanitize_response_data(r.text))
136 | elif (self._status_code == 500 or self._status_code ==
137 | 503) and self.handle_throttle:
138 | self._should_throttle = True
139 | self.response = PaymentErrorResponse(
140 | '{}'.format(r.status_code))
141 | else:
142 | self.response = PaymentErrorResponse(r.text)
143 | self.logger.debug('Response: %s',
144 | self._sanitize_response_data(r.text))
145 |
146 | def send_post(self):
147 | """Call request to send to MWS endpoint and handle throttle if set."""
148 | if self.handle_throttle:
149 | for retry_time in (0, 1, 4, 10):
150 | self._request(retry_time)
151 | if self.success or not self._should_throttle:
152 | break
153 | else:
154 | self._request(0)
155 |
156 | def _sanitize_request_data(self, text):
157 | editText = text
158 | patterns = []
159 | patterns.append(r'(?s)(SellerNote).*(&)')
160 | patterns.append(r'(?s)(SellerAuthorizationNote).*(&)')
161 | patterns.append(r'(?s)(SellerCaptureNote).*(&)')
162 | patterns.append(r'(?s)(SellerRefundNote).*(&)')
163 | replacement = r'\1 REMOVED \2'
164 |
165 | for pattern in patterns:
166 | editText = re.sub(pattern, replacement, editText)
167 | return editText
168 |
169 | def _sanitize_response_data(self, text):
170 | editText = text
171 | patterns = []
172 | patterns.append(r'(?s)().*()')
173 | patterns.append(r'(?s)().*()')
174 | patterns.append(r'(?s)().*(<\/BillingAddress>)')
175 | patterns.append(r'(?s)().*(<\/SellerNote>)')
176 | patterns.append(r'(?s)().*(<\/AuthorizationBillingAddress>)')
177 | patterns.append(r'(?s)().*(<\/SellerAuthorizationNote>)')
178 | patterns.append(r'(?s)().*(<\/SellerCaptureNote>)')
179 | patterns.append(r'(?s)().*(<\/SellerRefundNote>)')
180 | replacement = r'\1 REMOVED \2'
181 |
182 | for pattern in patterns:
183 | editText = re.sub(pattern, replacement, editText)
184 | return editText
--------------------------------------------------------------------------------
/amazon_pay/payment_response.py:
--------------------------------------------------------------------------------
1 | import re
2 | import json
3 | import xml.etree.ElementTree as et
4 | from collections import defaultdict
5 |
6 |
7 | class PaymentResponse:
8 |
9 | """Base class for all OffAmazonPayments responses
10 |
11 | Parameters
12 | ----------
13 | xml : string
14 | XML response from Amazon.
15 |
16 |
17 | Properties
18 | ----------
19 | root : string
20 | Root XML node.
21 |
22 | ns : string
23 | XML namespace.
24 |
25 | success : boolean
26 | Success or failure of the API call.
27 |
28 | response : Response
29 | Holds the response type for the API call.
30 |
31 | xml : string
32 | XML response from Amazon.
33 | """
34 |
35 | def __init__(self, xml):
36 | """Initialize response"""
37 | self.success = True
38 | self._xml = xml
39 | try:
40 | self._root = et.fromstring(xml)
41 | self._ns = self._namespace(self._root)
42 | self._response_type = self._root.tag.replace(self._ns, '')
43 | except:
44 | raise ValueError('Invalid XML.')
45 |
46 | """There is a bug where 'eu' endpoint returns ErrorResponse XML node
47 | 'RequestID' with capital 'ID'. 'na' endpoint returns 'RequestId'
48 | """
49 | try:
50 | if self._root.find('.//{}RequestId'.format(self._ns)) is None:
51 | self.request_id = self._root.find(
52 | './/{}RequestID'.format(self._ns)).text
53 | else:
54 | self.request_id = self._root.find(
55 | './/{}RequestId'.format(self._ns)).text
56 | except:
57 | self.request_id = None
58 |
59 | def _namespace(self, element):
60 | """Get XML namespace"""
61 | ns = re.match('\{.*\}', element.tag)
62 | return ns.group(0) if ns else ''
63 |
64 | def to_xml(self):
65 | """Return XML"""
66 | return self._xml
67 |
68 | def to_json(self):
69 | """Return JSON"""
70 | return json.dumps(self._etree_to_dict(self._root), ensure_ascii=False)
71 |
72 | def to_dict(self):
73 | """Return Dictionary"""
74 | return self._etree_to_dict(self._root)
75 |
76 | def _etree_to_dict(self, t):
77 | """Convert XML to Dictionary"""
78 | d = {t.tag.replace(self._ns, ''): {} if t.attrib else None}
79 | children = list(t)
80 | if children:
81 | dd = defaultdict(list)
82 | for dc in map(self._etree_to_dict, children):
83 | for k, v in dc.items():
84 | dd[k].append(v)
85 | d = {
86 | t.tag.replace(self._ns, ''): {
87 | k: v[0] if len(v) == 1 else v for k,
88 | v in dd.items()}}
89 | if t.attrib:
90 | d[t.tag.replace(self._ns, '')].update(('@' + k, v)
91 | for k, v in t.attrib.items())
92 | if t.text:
93 | text = t.text.strip()
94 | if children or t.attrib:
95 | if text:
96 | d[t.tag.replace(self._ns, '')]['#text'] = text
97 | else:
98 | d[t.tag.replace(self._ns, '')] = text
99 | return d
100 |
101 |
102 | class PaymentErrorResponse(PaymentResponse):
103 |
104 | """Error response subclass"""
105 |
106 | def __init__(self, xml):
107 |
108 | super(PaymentErrorResponse, self).__init__(xml)
109 | self.success = False
110 |
--------------------------------------------------------------------------------
/amazon_pay/version.py:
--------------------------------------------------------------------------------
1 | versions = {'api_version': '2013-01-01',
2 | 'application_version': '2.7.1'}
3 |
--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | addopts = --cov amazon_pay_sdk_python test
3 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | description-file = README.md
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 | import amazon_pay.version as pwa_version
3 |
4 | setup(
5 | name='amazon_pay',
6 | packages=['amazon_pay'],
7 | version=pwa_version.versions['application_version'],
8 | description='Amazon Pay Python SDK',
9 | url='https://github.com/amzn/amazon-pay-sdk-python',
10 | download_url='https://github.com/amzn/amazon-pay-sdk-python/tarball/{}'.format(
11 | pwa_version.versions['application_version']),
12 | author='EPS-DSE',
13 | author_email='amazon-pay-sdk@amazon.com',
14 | license='Apache License version 2.0, January 2004',
15 | install_requires=['pyOpenSSL >= 0.11',
16 | 'requests >= 2.6.0'],
17 | keywords=['Amazon', 'Payments', 'Login', 'Python', 'API', 'SDK'],
18 | classifiers=[
19 | 'Development Status :: 5 - Production/Stable',
20 | 'Intended Audience :: Developers',
21 | 'License :: OSI Approved :: Apache Software License',
22 | 'Natural Language :: English',
23 | 'Operating System :: OS Independent',
24 | 'Programming Language :: Python',
25 | 'Programming Language :: Python :: 3.2',
26 | 'Programming Language :: Python :: 3.4',
27 | 'Topic :: Software Development :: Libraries',
28 | 'Topic :: Software Development :: Libraries :: Python Modules']
29 | )
30 |
--------------------------------------------------------------------------------
/test/log.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Physical
5 | REMOVED
6 |
7 | London
8 | Chelsea
9 | GB
10 | SW3 4ND
11 |
12 |
13 | London
14 | Chelsea
15 | GB
16 | SW3 4ND
17 |
18 |
19 | 2017-04-03T15:36:30.897Z
20 |
21 | This is a seller note
22 | This is a seller note
23 | This is a seller note
24 | This is a seller note
25 |
26 | USD
27 | 19.95
28 |
29 | Sandbox
30 | S01-3616844-6294179
31 | 2016-10-05T15:36:30.897Z
32 | false
33 |
34 |
--------------------------------------------------------------------------------
/test/sanlog.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Physical
5 | REMOVED
6 | REMOVED
7 | REMOVED
8 |
9 | 2017-04-03T15:36:30.897Z
10 |
11 | REMOVED
12 | REMOVED
13 | REMOVED
14 | REMOVED
15 |
16 | USD
17 | 19.95
18 |
19 | Sandbox
20 | S01-3616844-6294179
21 | 2016-10-05T15:36:30.897Z
22 | false
23 |
24 |
--------------------------------------------------------------------------------
/test/test.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIFFzCCA/+gAwIBAgIQfXvtWTP5lfZLpmyNHLk1TDANBgkqhkiG9w0BAQUFADCB
3 | tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
4 | ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
5 | YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
6 | VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTQwODIz
7 | MDAwMDAwWhcNMTUwODIyMjM1OTU5WjBrMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
8 | V2FzaGluZ3RvbjEQMA4GA1UEBxQHU2VhdHRsZTEZMBcGA1UEChQQQW1hem9uLmNv
9 | bSwgSW5jLjEaMBgGA1UEAxQRc25zLmFtYXpvbmF3cy5jb20wggEiMA0GCSqGSIb3
10 | DQEBAQUAA4IBDwAwggEKAoIBAQDP/HD18qyBx4IgBvgVCkLTW18bULmoaaOQYtRY
11 | yVpPxIFkNSHxT4uYH9knKUqddKQd1TEXHh0bF50lBiHZpuascNc3+FP2YKbF2t/z
12 | a+zHfLipW01np85VDdIWedvB9TpnMdY9PQYTVx41+2fnei9WgjwXVM085WRSECh3
13 | aRdkvOjwTN/Tlrgy3hoebVN3V5kB67b139m3xAlZjoB8MPdk/tlsk+wgVxuAY/gz
14 | xGIZRJxlgEtsu2g8+rDkjS2tk3457Cz8aXRZSCGi+BB6yN2WhvWwPzSDJMDKxwXY
15 | I8fGw0xutF4WHN414KBUp/s/+E6Ib7GxLUCwFon1swKRz9NxAgMBAAGjggFqMIIB
16 | ZjAcBgNVHREEFTATghFzbnMuYW1hem9uYXdzLmNvbTAJBgNVHRMEAjAAMA4GA1Ud
17 | DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwZQYDVR0g
18 | BF4wXDBaBgpghkgBhvhFAQc2MEwwIwYIKwYBBQUHAgEWF2h0dHBzOi8vZC5zeW1j
19 | Yi5jb20vY3BzMCUGCCsGAQUFBwICMBkaF2h0dHBzOi8vZC5zeW1jYi5jb20vcnBh
20 | MB8GA1UdIwQYMBaAFA1EXBZTRMGCfh0gqyX0AWPYvnmlMCsGA1UdHwQkMCIwIKAe
21 | oByGGmh0dHA6Ly9zZC5zeW1jYi5jb20vc2QuY3JsMFcGCCsGAQUFBwEBBEswSTAf
22 | BggrBgEFBQcwAYYTaHR0cDovL3NkLnN5bWNkLmNvbTAmBggrBgEFBQcwAoYaaHR0
23 | cDovL3NkLnN5bWNiLmNvbS9zZC5jcnQwDQYJKoZIhvcNAQEFBQADggEBABm5RaeH
24 | sLtJftDeGghHAUkco8wkCshKQO1obhuMDJkJHAHbUrweP4Gw7WRhHNHo1jgA6Q61
25 | NXik2w6H7SDVngCVIqOLFsr1DQ7fB5oSevMwjLvlaLxWBAvgKPSsjCt+QF1aNQiv
26 | sfhOIgiJZTObBERsSs9FXWQ/vMkoisPKYJGn3KigzZj0GXSAB0do8Ejq8siBczgM
27 | 9o8NixqVvD7AOoE/QWXCtRDMRnQ5oIAc/Q9iUCJ8oAlbljDFkSqwgunpaG0iuHQZ
28 | 6Q4iHgmrgHM302H9fFxupyW46zwLH9gmbrUulmLRZT14bIzd0cZXqIl6nd8il4nq
29 | kqQRCW8P2LDot4s=
30 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/test/test_ap.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import json
4 | import platform
5 | import unittest
6 | import xml.etree.ElementTree as et
7 | import amazon_pay.ap_region as ap_region
8 | import amazon_pay.version as ap_version
9 | from unittest.mock import Mock, patch
10 | from amazon_pay.client import AmazonPayClient
11 | from amazon_pay.payment_request import PaymentRequest
12 | from amazon_pay.payment_response import PaymentResponse, PaymentErrorResponse
13 | from symbol import parameters
14 |
15 | class AmazonPayClientTest(unittest.TestCase):
16 |
17 | def setUp(self):
18 | self.maxDiff = None
19 | self.mws_access_key = 'mws_access_key'
20 | self.mws_secret_key = 'mws_secret_key'
21 | self.merchant_id = 'merchant_id'
22 | self.service_version = '2013-01-01'
23 | self.mws_endpoint = \
24 | 'https://mws.amazonservices.com/OffAmazonPayments_Sandbox/{}'.format(
25 | self.service_version)
26 |
27 | self.client = AmazonPayClient(
28 | mws_access_key=self.mws_access_key,
29 | mws_secret_key=self.mws_secret_key,
30 | merchant_id=self.merchant_id,
31 | handle_throttle=False,
32 | sandbox=True,
33 | region='na',
34 | currency_code='USD')
35 |
36 | self.request = PaymentRequest(
37 | params={'test': 'test'},
38 | config={'mws_access_key': self.mws_access_key,
39 | 'mws_secret_key': self.mws_secret_key,
40 | 'api_version': '2013-01-01',
41 | 'merchant_id': self.merchant_id,
42 | 'mws_endpoint': self.mws_endpoint,
43 | 'headers': {'test': 'test'},
44 | 'handle_throttle': True})
45 |
46 | self.response = PaymentResponse('الفلانية فلا')
47 | self.supplementary_data = '{"AirlineMetaData" : {"version": 1.0, "airlineCode": "PAX", "flightDate": "2018-03-24T20:29:19.22Z", "departureAirport": "CDG", "destinationAirport": "LUX", "bookedLastTime": -1, "classOfTravel": "F", "passengers": {"numberOfPassengers": 4, "numberOfChildren": 1, "numberOfInfants": 1 }}, "AccommodationMetaData": {"version": 1.0, "startDate": "2018-03-24T20:29:19.22Z", "endDate": "2018-03-24T20:29:19.22Z", "lengthOfStay": 5, "numberOfGuests": 4, "class": "Standard", "starRating": 5, "bookedLastTime": -1 }, "OrderMetaData": {"version": 1.0, "numberOfItems": 3, "type": "Digital" }, "BuyerMetaData": {"version" : 1.0, "isFirstTimeCustomer" : true, "numberOfPastPurchases" : 2, "numberOfDisputedPurchases" : 3, "hasOpenDispute" : true, "riskScore" : 0.75 }}'
48 |
49 | def mock_requests_post(self, url, data=None, headers=None, verify=False):
50 | mock_response = Mock()
51 | mock_response.text = '\
52 | \
53 | Draft\
54 | \
55 | \
56 | '
57 | mock_response.status_code = 200
58 | return mock_response
59 |
60 | def mock_requests_500_post(
61 | self, url, data=None, headers=None, verify=False):
62 | mock_response = Mock()
63 | mock_response.text = 'test'
64 | mock_response.status_code = 500
65 | return mock_response
66 |
67 | def mock_requests_generic_error_post(
68 | self, url, data=None, headers=None, verify=False):
69 | mock_response = Mock()
70 | mock_response.text = 'test'
71 | mock_response.status_code = 502
72 | return mock_response
73 |
74 | def mock_requests_503_post(
75 | self, url, data=None, headers=None, verify=False):
76 | mock_response = Mock()
77 | mock_response.text = 'test'
78 | mock_response.status_code = 503
79 | return mock_response
80 |
81 | def mock_get_login_profile(self, url, headers, params, verify):
82 | mock_response = Mock()
83 | mock_response.json.return_value = {"aud": "client_id"}
84 | mock_response.status_code = 200
85 | return mock_response
86 |
87 | def test_sandbox_setter(self):
88 | self.client.sandbox = False
89 | self.assertEqual(
90 | self.client._mws_endpoint,
91 | 'https://mws.amazonservices.com/OffAmazonPayments/2013-01-01')
92 | self.client.sandbox = True
93 | self.assertEqual(
94 | self.client._mws_endpoint,
95 | 'https://mws.amazonservices.com/OffAmazonPayments_Sandbox/2013-01-01')
96 |
97 | def test_sanitize_response_data(self):
98 | current_file_dir = os.path.dirname(__file__)
99 | test_file_path = os.path.join(current_file_dir, "log.txt")
100 | f = open(test_file_path, "r")
101 | source_text = f.read()
102 | f.close()
103 | text = self.request._sanitize_response_data(source_text)
104 | test_file_path = os.path.join(current_file_dir, "sanlog.txt")
105 | f = open(test_file_path, "r")
106 | san_text = f.read()
107 | f.close
108 | self.assertEqual(text, san_text)
109 |
110 | def test_region_exception(self):
111 | with self.assertRaises(KeyError):
112 | AmazonPayClient(
113 | mws_access_key=self.mws_access_key,
114 | mws_secret_key=self.mws_secret_key,
115 | merchant_id=self.merchant_id,
116 | handle_throttle=False,
117 | sandbox=True,
118 | region='should_throw_exception',
119 | currency_code='test')
120 |
121 | def test_set_endpoint(self):
122 | self.client._set_endpoint()
123 | self.assertEqual(
124 | self.client._mws_endpoint,
125 | 'https://mws.amazonservices.com/OffAmazonPayments_Sandbox/2013-01-01')
126 |
127 | def test_sign(self):
128 | test_signature = self.request._sign('my_test_string')
129 | self.assertEqual(
130 | test_signature,
131 | 'JQZYxe8EFlLE3XCAWotsn329rpZF7OFYhA8oo7rUV2E=')
132 |
133 | def test_application_settings(self):
134 | client = AmazonPayClient(
135 | mws_access_key=self.mws_access_key,
136 | mws_secret_key=self.mws_secret_key,
137 | merchant_id=self.merchant_id,
138 | handle_throttle=False,
139 | sandbox=True,
140 | region='na',
141 | currency_code='USD',
142 | application_name='test_application',
143 | application_version='test_application_version')
144 | self.assertEqual(client.application_name, 'test_application')
145 | self.assertEqual(
146 | client.application_version,
147 | 'test_application_version')
148 |
149 | def test_properties(self):
150 | self.assertEqual(self.client.mws_access_key, 'mws_access_key')
151 | self.assertEqual(self.client.mws_secret_key, 'mws_secret_key')
152 | self.assertEqual(self.client.merchant_id, 'merchant_id')
153 | self.assertEqual(self.client._region_code, 'na')
154 | self.assertEqual(self.client.currency_code, 'USD')
155 | self.assertEqual(self.client.handle_throttle, False)
156 | self.assertEqual(self.client.sandbox, True)
157 |
158 | @patch('requests.post')
159 | def test_generic_error_response(self, mock_urlopen):
160 | mock_urlopen.side_effect = self.mock_requests_generic_error_post
161 | self.request.send_post()
162 | response = self.request.response
163 | self.assertEqual(type(response), PaymentErrorResponse)
164 |
165 | @patch('requests.post')
166 | def test_500_response(self, mock_urlopen):
167 | mock_urlopen.side_effect = self.mock_requests_500_post
168 | self.request.send_post()
169 | response = self.request.response.to_dict()
170 | self.assertEqual(response['error'], '500')
171 |
172 | @patch('requests.post')
173 | def test_503_response(self, mock_urlopen):
174 | mock_urlopen.side_effect = self.mock_requests_503_post
175 | self.request.send_post()
176 | response = self.request.response.to_dict()
177 | self.assertEqual(response['error'], '503')
178 |
179 | @patch('requests.post')
180 | def test_headers(self, mock_urlopen):
181 | py_version = ".".join(map(str, sys.version_info[:3]))
182 | mock_urlopen.side_effect = self.mock_requests_post
183 | self.client.get_service_status()
184 | if sys.version_info[0] == 3 and sys.version_info[1] >= 2:
185 | py_valid = True
186 |
187 | header_expected = {
188 | 'Content-Type': 'application/x-www-form-urlencoded',
189 | "User-Agent":'amazon-pay-sdk-python/{0} ({1}Python/{2}; {3}/{4})'.format(
190 | str(ap_version.versions['application_version']),
191 | (''),
192 | py_version,
193 | str(platform.system()),
194 | str(platform.release())
195 | )
196 | }
197 | self.assertEqual(mock_urlopen.call_args[1]['headers'], header_expected)
198 | self.assertTrue(py_valid, True)
199 |
200 | @patch('requests.post')
201 | def test_get_merchant_account_status(self, mock_urlopen):
202 | mock_urlopen.side_effect = self.mock_requests_post
203 | self.client.get_merchant_account_status(
204 | merchant_id='A2AMGDUDUJFL',
205 | mws_auth_token='amzn.mws.d8f2d-6a5f-b46293482379')
206 | parameters = {
207 | 'Action': 'GetMerchantAccountStatus',
208 | 'SellerId': 'A2AMGDUDUJFL',
209 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b46293482379'}
210 | data_expected = self.request._querystring(parameters)
211 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
212 |
213 | @patch('requests.post')
214 | def test_create_order_reference_for_id(self, mock_urlopen):
215 | mock_urlopen.side_effect = self.mock_requests_post
216 | self.client.create_order_reference_for_id(
217 | object_id='B01-462347-4762387',
218 | object_id_type='BillingAgreement',
219 | order_total='1',
220 | inherit_shipping_address=False,
221 | confirm_now=True,
222 | platform_id='testPlatformId123',
223 | seller_note='testSellerNote2145',
224 | seller_order_id='testSellerOrderId21434',
225 | supplementary_data=self.supplementary_data,
226 | store_name='testStoreName1234',
227 | custom_information='testCustomInfo12435',
228 | merchant_id='A2AMR0DUGHIUEHQ',
229 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06476237468923749823')
230 | parameters = {
231 | 'Action': 'CreateOrderReferenceForId',
232 | 'Id': 'B01-462347-4762387',
233 | 'IdType': 'BillingAgreement',
234 | 'OrderReferenceAttributes.OrderTotal.Amount': '1',
235 | 'OrderReferenceAttributes.OrderTotal.CurrencyCode': 'USD',
236 | 'InheritShippingAddress': 'false',
237 | 'ConfirmNow': 'true',
238 | 'OrderReferenceAttributes.PlatformId': 'testPlatformId123',
239 | 'OrderReferenceAttributes.SellerNote': 'testSellerNote2145',
240 | 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId': 'testSellerOrderId21434',
241 | 'OrderReferenceAttributes.SupplementaryData': self.supplementary_data,
242 | 'OrderReferenceAttributes.SellerOrderAttributes.StoreName': 'testStoreName1234',
243 | 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation': 'testCustomInfo12435',
244 | 'SellerId': 'A2AMR0DUGHIUEHQ',
245 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06476237468923749823'}
246 |
247 | data_expected = self.request._querystring(parameters)
248 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
249 |
250 | @patch('requests.post')
251 | def test_get_billing_agreement_details(self, mock_urlopen):
252 | mock_urlopen.side_effect = self.mock_requests_post
253 | self.client.get_billing_agreement_details(
254 | amazon_billing_agreement_id='B01-47236478-46253862',
255 | address_consent_token='AFYDFWIGHUIP',
256 | merchant_id='ADEIUYIOQUIOW',
257 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-7462348237498')
258 | parameters = {
259 | 'Action': 'GetBillingAgreementDetails',
260 | 'AmazonBillingAgreementId': 'B01-47236478-46253862',
261 | 'AddressConsentToken': 'AFYDFWIGHUIP',
262 | 'SellerId': 'ADEIUYIOQUIOW',
263 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-7462348237498'}
264 | data_expected = self.request._querystring(parameters)
265 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
266 |
267 | @patch('requests.post')
268 | def test_set_billing_agreement_details(self, mock_urlopen):
269 | mock_urlopen.side_effect = self.mock_requests_post
270 | self.client.set_billing_agreement_details(
271 | amazon_billing_agreement_id='B01-47236478-462863428',
272 | platform_id='testPlatformId89',
273 | seller_note='testSellerNote3251',
274 | seller_billing_agreement_id='testBillingAgreement1213',
275 | store_name='testStoreName5237',
276 | custom_information='testCustomInfo32365',
277 | merchant_id='AGDUIEJOQEOPQWIKO',
278 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-bc12-4623862')
279 | parameters = {
280 | 'Action': 'SetBillingAgreementDetails',
281 | 'AmazonBillingAgreementId': 'B01-47236478-462863428',
282 | 'BillingAgreementAttributes.PlatformId': 'testPlatformId89',
283 | 'BillingAgreementAttributes.SellerNote': 'testSellerNote3251',
284 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.SellerBillingAgreementId': 'testBillingAgreement1213',
285 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.StoreName': 'testStoreName5237',
286 | 'BillingAgreementAttributes.SellerBillingAgreementAttributes.CustomInformation': 'testCustomInfo32365',
287 | 'SellerId': 'AGDUIEJOQEOPQWIKO',
288 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-bc12-4623862'}
289 | data_expected = self.request._querystring(parameters)
290 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
291 |
292 | @patch('requests.post')
293 | def test_confirm_billing_agreement(self, mock_urlopen):
294 | mock_urlopen.side_effect = self.mock_requests_post
295 | self.client.confirm_billing_agreement(
296 | amazon_billing_agreement_id='B01-47236478-46284638789',
297 | merchant_id='AGFUHWIEJLMLK',
298 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-bc12-4263289')
299 | parameters = {
300 | 'Action': 'ConfirmBillingAgreement',
301 | 'AmazonBillingAgreementId': 'B01-47236478-46284638789',
302 | 'SellerId': 'AGFUHWIEJLMLK',
303 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-bc12-4263289'}
304 | data_expected = self.request._querystring(parameters)
305 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
306 |
307 | @patch('requests.post')
308 | def test_validate_billing_agreement(self, mock_urlopen):
309 | mock_urlopen.side_effect = self.mock_requests_post
310 | self.client.validate_billing_agreement(
311 | amazon_billing_agreement_id='B01-47236478-46287462347823490',
312 | merchant_id='AGFUHWHYDIIJQWL',
313 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-bc12-457267342897')
314 | parameters = {
315 | 'Action': 'ValidateBillingAgreement',
316 | 'AmazonBillingAgreementId': 'B01-47236478-46287462347823490',
317 | 'SellerId': 'AGFUHWHYDIIJQWL',
318 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-bc12-457267342897'}
319 | data_expected = self.request._querystring(parameters)
320 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
321 |
322 | @patch('requests.post')
323 | def test_authorize_on_billing_agreement(self, mock_urlopen):
324 | mock_urlopen.side_effect = self.mock_requests_post
325 | self.client.authorize_on_billing_agreement(
326 | amazon_billing_agreement_id='B01-4653268-47632947',
327 | authorization_reference_id='testAuthRefId31253',
328 | authorization_amount='1',
329 | seller_authorization_note='testSellerAuthNote3612367',
330 | transaction_timeout=0,
331 | capture_now=True,
332 | soft_descriptor='testSoftDescriptor42837',
333 | seller_note='testSellerNote4721893',
334 | platform_id='testPlatformId47237',
335 | seller_order_id='testSellerOrderId4237',
336 | store_name='testStoreName842398',
337 | custom_information='testCustomInfo623',
338 | supplementary_data=self.supplementary_data,
339 | inherit_shipping_address=False,
340 | merchant_id='A2AMR0FDYHGHJD',
341 | mws_auth_token='amzn.mws.d6ac8f2d-463286-fhegsdj46238')
342 | parameters = {
343 | 'Action': 'AuthorizeOnBillingAgreement',
344 | 'AmazonBillingAgreementId': 'B01-4653268-47632947',
345 | 'TransactionTimeout': '0',
346 | 'AuthorizationReferenceId': 'testAuthRefId31253',
347 | 'AuthorizationAmount.Amount': '1',
348 | 'AuthorizationAmount.CurrencyCode': 'USD',
349 | 'CaptureNow': 'true',
350 | 'SellerAuthorizationNote': 'testSellerAuthNote3612367',
351 | 'SoftDescriptor': 'testSoftDescriptor42837',
352 | 'SellerNote': 'testSellerNote4721893',
353 | 'PlatformId': 'testPlatformId47237',
354 | 'InheritShippingAddress': 'false',
355 | 'SellerOrderAttributes.SellerOrderId': 'testSellerOrderId4237',
356 | 'SellerOrderAttributes.StoreName': 'testStoreName842398',
357 | 'SellerOrderAttributes.CustomInformation': 'testCustomInfo623',
358 | 'SellerOrderAttributes.SupplementaryData': self.supplementary_data,
359 | 'SellerId': 'A2AMR0FDYHGHJD',
360 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-463286-fhegsdj46238'}
361 | data_expected = self.request._querystring(parameters)
362 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
363 |
364 | @patch('requests.post')
365 | def test_close_billing_agreement(self, mock_urlopen):
366 | mock_urlopen.side_effect = self.mock_requests_post
367 | self.client.close_billing_agreement(
368 | amazon_billing_agreement_id='B01-4236278-3761372',
369 | closure_reason='testClosureReason',
370 | merchant_id='A2AMR0DGUQHWIJQWL',
371 | mws_auth_token='amzn.mws.d6ac8f2d-463286-fhegsdj46238')
372 | parameters = {
373 | 'Action': 'CloseBillingAgreement',
374 | 'AmazonBillingAgreementId': 'B01-4236278-3761372',
375 | 'ClosureReason': 'testClosureReason',
376 | 'SellerId': 'A2AMR0DGUQHWIJQWL',
377 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-463286-fhegsdj46238'}
378 | data_expected = self.request._querystring(parameters)
379 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
380 |
381 | @patch('requests.post')
382 | def test_set_order_reference_details(self, mock_urlopen):
383 | mock_urlopen.side_effect = self.mock_requests_post
384 | self.client.set_order_reference_details(
385 | amazon_order_reference_id='P01-1234567-7654897',
386 | order_total='1',
387 | platform_id='platformId4673',
388 | seller_note='sellerNote38278',
389 | seller_order_id='sellerOrderId123',
390 | store_name='testStoreName387289',
391 | custom_information='customInfo34278',
392 | merchant_id='A2AMR0CLHYUTGH',
393 | mws_auth_token='amzn.mws.d8f2d-6a5f-b06a4628',
394 | supplementary_data=self.supplementary_data)
395 | parameters = {
396 | 'Action': 'SetOrderReferenceDetails',
397 | 'AmazonOrderReferenceId': 'P01-1234567-7654897',
398 | 'OrderReferenceAttributes.OrderTotal.Amount': '1',
399 | 'OrderReferenceAttributes.OrderTotal.CurrencyCode': 'USD',
400 | 'OrderReferenceAttributes.PlatformId': 'platformId4673',
401 | 'OrderReferenceAttributes.SellerNote': 'sellerNote38278',
402 | 'OrderReferenceAttributes.SellerOrderAttributes.SellerOrderId': 'sellerOrderId123',
403 | 'OrderReferenceAttributes.SellerOrderAttributes.StoreName': 'testStoreName387289',
404 | 'OrderReferenceAttributes.SellerOrderAttributes.CustomInformation': 'customInfo34278',
405 | 'SellerId': 'A2AMR0CLHYUTGH',
406 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b06a4628',
407 | 'OrderReferenceAttributes.SellerOrderAttributes.SupplementaryData': self.supplementary_data}
408 | data_expected = self.request._querystring(parameters)
409 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
410 |
411 | @patch('requests.post')
412 | def test_set_order_attributes(self, mock_urlopen):
413 | mock_urlopen.side_effect = self.mock_requests_post
414 | self.client.set_order_attributes(
415 | amazon_order_reference_id='P01-1234567-4827348237',
416 | currency_code='USD',
417 | amount='1',
418 | seller_order_id='testSellerOrderId5371',
419 | payment_service_provider_id='AGHJHHJKJHL',
420 | payment_service_provider_order_id='testPSPOrderId',
421 | platform_id='testPlatformId472',
422 | seller_note='testSellerNote4628',
423 | request_payment_authorization='true',
424 | store_name='testStoreName26157',
425 | list_order_item_categories=['test'],
426 | custom_information='testCustomInfo35273',
427 | merchant_id='AGHJHHJKJHL',
428 | mws_auth_token='amzn.mws.d8f2d-6a5f-b06a4628',
429 | supplementary_data=self.supplementary_data)
430 |
431 | parameters = {
432 | 'Action': 'SetOrderAttributes',
433 | 'AmazonOrderReferenceId': 'P01-1234567-4827348237',
434 | 'OrderAttributes.OrderTotal.Amount': '1',
435 | 'OrderAttributes.OrderTotal.CurrencyCode': 'USD',
436 | 'OrderAttributes.SellerOrderAttributes.CustomInformation': 'testCustomInfo35273',
437 | 'OrderAttributes.SellerOrderAttributes.OrderItemCategories.OrderItemCategory.1': 'test',
438 | 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderId': 'AGHJHHJKJHL',
439 | 'OrderAttributes.PaymentServiceProviderAttributes.PaymentServiceProviderOrderId': 'testPSPOrderId',
440 | 'OrderAttributes.PlatformId': 'testPlatformId472',
441 | 'OrderAttributes.RequestPaymentAuthorization': 'true',
442 | 'OrderAttributes.SellerNote': 'testSellerNote4628',
443 | 'OrderAttributes.SellerOrderAttributes.SellerOrderId': 'testSellerOrderId5371',
444 | 'OrderAttributes.SellerOrderAttributes.StoreName': 'testStoreName26157',
445 | 'SellerId': 'AGHJHHJKJHL',
446 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b06a4628',
447 | 'OrderAttributes.SellerOrderAttributes.SupplementaryData': self.supplementary_data}
448 | data_expected = self.request._querystring(parameters)
449 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
450 |
451 |
452 | @patch('requests.post')
453 | def test_get_order_reference_details(self, mock_urlopen):
454 | mock_urlopen.side_effect = self.mock_requests_post
455 | self.client.get_order_reference_details(
456 | amazon_order_reference_id='P01-476238-47238',
457 | address_consent_token='ADUHIQILPLP',
458 | access_token='AHJJOKJJHNJNJK',
459 | merchant_id='ADGJUHJWKJKJ',
460 | mws_auth_token='amzn.mws.d8f2d-6a5f-b427489234798')
461 | parameters = {
462 | 'Action': 'GetOrderReferenceDetails',
463 | 'AmazonOrderReferenceId': 'P01-476238-47238',
464 | 'AddressConsentToken': 'ADUHIQILPLP',
465 | 'AccessToken': 'AHJJOKJJHNJNJK',
466 | 'SellerId': 'ADGJUHJWKJKJ',
467 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b427489234798'}
468 | data_expected = self.request._querystring(parameters)
469 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
470 |
471 | @patch('requests.post')
472 | def test_confirm_order_reference(self, mock_urlopen):
473 | mock_urlopen.side_effect = self.mock_requests_post
474 | self.client.confirm_order_reference(
475 | amazon_order_reference_id='P01-476238-47263849238',
476 | merchant_id='AHDGJHDJKFJIIIJ',
477 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42rwe74237489',
478 | success_url='https://www.success.com',
479 | failure_url='https://www.failure.com',
480 | authorization_amount='5',
481 | currency_code='USD'
482 | )
483 |
484 | parameters = {
485 | 'Action': 'ConfirmOrderReference',
486 | 'AmazonOrderReferenceId': 'P01-476238-47263849238',
487 | 'SellerId': 'AHDGJHDJKFJIIIJ',
488 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42rwe74237489',
489 | 'SuccessUrl': 'https://www.success.com',
490 | 'FailureUrl': 'https://www.failure.com',
491 | 'AuthorizationAmount.Amount': '5',
492 | 'AuthorizationAmount.CurrencyCode': 'USD'
493 | }
494 |
495 | data_expected = self.request._querystring(parameters)
496 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
497 |
498 | @patch('requests.post')
499 | def test_confirm_order_reference_with_expect_immediate_authorization_as_true(self, mock_urlopen):
500 | mock_urlopen.side_effect = self.mock_requests_post
501 | self.client.confirm_order_reference(
502 | amazon_order_reference_id='P02-6009038-6480465',
503 | merchant_id='AWBW6G04XFUTG',
504 | expect_immediate_authorization=True)
505 | parameters = {
506 | 'Action': 'ConfirmOrderReference',
507 | 'AmazonOrderReferenceId': 'P02-6009038-6480465',
508 | 'SellerId': 'AWBW6G04XFUTG',
509 | 'ExpectImmediateAuthorization': 'true'}
510 | data_expected = self.request._querystring(parameters)
511 | # print(data_expected)
512 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
513 |
514 | @patch('requests.post')
515 | def test_confirm_order_reference_with_expect_immediate_authorization_as_false(self, mock_urlopen):
516 | mock_urlopen.side_effect = self.mock_requests_post
517 | self.client.confirm_order_reference(
518 | amazon_order_reference_id='P02-0736942-3399325',
519 | merchant_id='AWBW6G04XFUTG',
520 | expect_immediate_authorization=False)
521 | parameters = {
522 | 'Action': 'ConfirmOrderReference',
523 | 'AmazonOrderReferenceId': 'P02-0736942-3399325',
524 | 'SellerId': 'AWBW6G04XFUTG',
525 | 'ExpectImmediateAuthorization': 'false'}
526 | data_expected = self.request._querystring(parameters)
527 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
528 |
529 | @patch('requests.post')
530 | def test_cancel_order_reference(self, mock_urlopen):
531 | mock_urlopen.side_effect = self.mock_requests_post
532 | self.client.cancel_order_reference(
533 | amazon_order_reference_id='P01-476238-472642737489',
534 | cancelation_reason='testCancelReason',
535 | merchant_id='AJHDELWJEKELW',
536 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42rw72372897893')
537 | parameters = {
538 | 'Action': 'CancelOrderReference',
539 | 'AmazonOrderReferenceId': 'P01-476238-472642737489',
540 | 'CancelationReason': 'testCancelReason',
541 | 'SellerId': 'AJHDELWJEKELW',
542 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42rw72372897893'}
543 | data_expected = self.request._querystring(parameters)
544 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
545 |
546 | @patch('requests.post')
547 | def test_close_order_reference(self, mock_urlopen):
548 | mock_urlopen.side_effect = self.mock_requests_post
549 | self.client.close_order_reference(
550 | amazon_order_reference_id='P01-476238-472642737489',
551 | closure_reason='testClosureReason24156',
552 | merchant_id='AJHYJHJLYFYGTUHK',
553 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42ryurueruio3uio87')
554 | parameters = {
555 | 'Action': 'CloseOrderReference',
556 | 'AmazonOrderReferenceId': 'P01-476238-472642737489',
557 | 'ClosureReason': 'testClosureReason24156',
558 | 'SellerId': 'AJHYJHJLYFYGTUHK',
559 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42ryurueruio3uio87'}
560 | data_expected = self.request._querystring(parameters)
561 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
562 |
563 | @patch('requests.post')
564 | def test_list_order_reference(self, mock_urlopen):
565 | mock_urlopen.side_effect = self.mock_requests_post
566 | self.client.list_order_reference(
567 | query_id='testSellerOrderId124',
568 | query_id_type='SellerOrderId',
569 | created_time_range_start='testStart',
570 | created_time_range_end='testEnd',
571 | sort_order='ascending',
572 | page_size=1,
573 | merchant_id='AFHRWKJEKJLJKL',
574 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42ryurueruio3uio87',
575 | order_reference_status_list_filter=['test1', 'test2'])
576 |
577 | if self.client.region in ('na'):
578 | payment_domain = 'NA_USD'
579 | elif self.client.region in ('uk', 'gb'):
580 | payment_domain = 'EU_GBP'
581 | elif self.client.region in ('jp', 'fe'):
582 | payment_domain = 'FE_JPY'
583 | elif self.client.region in ('eu', 'de', 'fr', 'it', 'es', 'cy'):
584 | payment_domain = 'EU_EUR'
585 | else:
586 | raise ValueError("Error. The current region code does not match our records")
587 |
588 |
589 | parameters = {
590 | 'Action': 'ListOrderReference',
591 | 'QueryId': 'testSellerOrderId124',
592 | 'QueryIdType': 'SellerOrderId',
593 | 'PaymentDomain': payment_domain,
594 | 'CreatedTimeRange.StartTime': 'testStart',
595 | 'CreatedTimeRange.EndTime': 'testEnd',
596 | 'SortOrder': 'ascending',
597 | 'PageSize': 1,
598 | 'SellerId': 'AFHRWKJEKJLJKL',
599 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42ryurueruio3uio87',
600 | 'OrderReferenceStatusListFilter.OrderReferenceStatus.1': 'test1',
601 | 'OrderReferenceStatusListFilter.OrderReferenceStatus.2': 'test2'}
602 | data_expected = self.request._querystring(parameters)
603 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
604 |
605 | @patch('requests.post')
606 | def test_list_order_reference_time_check_error(self, mock_urlopen):
607 | mock_urlopen.side_effect = self.mock_requests_generic_error_post
608 | self.client.list_order_reference(
609 | query_id='testSellerOrderId12444',
610 | query_id_type='SellerOrderId',
611 | created_time_range_start='testStart',
612 | created_time_range_end=None,
613 | sort_order=None,
614 | page_size=None,
615 | merchant_id='AGDJHKWJLHHK',
616 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42r23564783492380',
617 | order_reference_status_list_filter=None)
618 |
619 | if self.client.region in ('na'):
620 | payment_domain = 'NA_USD'
621 | elif self.client.region in ('uk', 'gb'):
622 | payment_domain = 'EU_GBP'
623 | elif self.client.region in ('jp', 'fe'):
624 | payment_domain = 'FE_JPY'
625 | elif self.client.region in ('eu', 'de', 'fr', 'it', 'es', 'cy'):
626 | payment_domain = 'EU_EUR'
627 | else:
628 | raise ValueError("Error. The current region code does not match our records")
629 |
630 |
631 | parameters = {
632 | 'Action': 'ListOrderReference',
633 | 'QueryId': 'testSellerOrderId12444',
634 | 'QueryIdType': 'SellerOrderId',
635 | 'PaymentDomain': payment_domain,
636 | 'SellerId': 'AGDJHKWJLHHK',
637 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42r23564783492380',
638 | 'CreatedTimeRange.StartTime': 'testStart'}
639 | data_expected = self.request._querystring(parameters)
640 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
641 |
642 | @patch('requests.post')
643 | def test_list_order_reference_by_next_token(self, mock_urlopen):
644 | mock_urlopen.side_effect = self.mock_requests_post
645 | self.client.list_order_reference_by_next_token(
646 | next_page_token='yrtewyy4823749329482394023940',
647 | merchant_id='AHFUHWJELWJELEJW',
648 | mws_auth_token='amzn.mws.d8f2d-6a5f-b42r23436248623748')
649 | parameters= {
650 | 'Action': 'ListOrderReferenceByNextToken',
651 | 'NextPageToken': 'yrtewyy4823749329482394023940',
652 | 'SellerId': 'AHFUHWJELWJELEJW',
653 | 'MWSAuthToken': 'amzn.mws.d8f2d-6a5f-b42r23436248623748'}
654 | data_expected = self.request._querystring(parameters)
655 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
656 |
657 | @patch('requests.post')
658 | def test_authorize(self, mock_urlopen):
659 | mock_urlopen.side_effect = self.mock_requests_post
660 | self.client.authorize(
661 | amazon_order_reference_id='P01-351-461238848937',
662 | authorization_reference_id='testAuthId123',
663 | authorization_amount='1',
664 | seller_authorization_note='testAuthNote123',
665 | transaction_timeout=0,
666 | capture_now=True,
667 | soft_descriptor='testSoftDescriptor12',
668 | merchant_id='A2AMR0CUYDHYIOW',
669 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-bc3276378843298-fgeswyd')
670 | parameters = {
671 | 'Action': 'Authorize',
672 | 'AmazonOrderReferenceId': 'P01-351-461238848937',
673 | 'AuthorizationReferenceId': 'testAuthId123',
674 | 'AuthorizationAmount.Amount': '1',
675 | 'AuthorizationAmount.CurrencyCode': 'USD',
676 | 'SellerAuthorizationNote': 'testAuthNote123',
677 | 'TransactionTimeout': '0',
678 | 'CaptureNow': 'true',
679 | 'SoftDescriptor': 'testSoftDescriptor12',
680 | 'SellerId': 'A2AMR0CUYDHYIOW',
681 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-bc3276378843298-fgeswyd'}
682 | data_expected = self.request._querystring(parameters)
683 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
684 |
685 | @patch('requests.post')
686 | def test_get_authorization_details(self, mock_urlopen):
687 | mock_urlopen.side_effect = self.mock_requests_post
688 | self.client.get_authorization_details(
689 | amazon_authorization_id='P01-351-461238848937-A42374987239849',
690 | merchant_id='AGDFHGWEHGWJH',
691 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-bc412328378')
692 | parameters = {
693 | 'Action': 'GetAuthorizationDetails',
694 | 'AmazonAuthorizationId': 'P01-351-461238848937-A42374987239849',
695 | 'SellerId': 'AGDFHGWEHGWJH',
696 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-bc412328378'}
697 | data_expected = self.request._querystring(parameters)
698 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
699 |
700 | @patch('requests.post')
701 | def test_capture(self, mock_urlopen):
702 | mock_urlopen.side_effect = self.mock_requests_post
703 | self.client.capture(
704 | amazon_authorization_id='P01-1234567-7654321-A467823648',
705 | capture_reference_id='testCaptureRefId123',
706 | capture_amount='1',
707 | seller_capture_note='testCaptureNote124',
708 | soft_descriptor='testSoftDescriptor123',
709 | merchant_id='A2AMR8YRGWKHK',
710 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b06a-472637-753648')
711 | parameters = {
712 | 'Action': 'Capture',
713 | 'AmazonAuthorizationId': 'P01-1234567-7654321-A467823648',
714 | 'CaptureReferenceId': 'testCaptureRefId123',
715 | 'CaptureAmount.Amount': '1',
716 | 'CaptureAmount.CurrencyCode': 'USD',
717 | 'SellerCaptureNote': 'testCaptureNote124',
718 | 'SoftDescriptor': 'testSoftDescriptor123',
719 | 'SellerId': 'A2AMR8YRGWKHK',
720 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b06a-472637-753648'}
721 | data_expected = self.request._querystring(parameters)
722 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
723 |
724 | @patch('requests.post')
725 | def test_get_capture_details(self, mock_urlopen):
726 | mock_urlopen.side_effect = self.mock_requests_post
727 | self.client.get_capture_details(
728 | amazon_capture_id='P01-4763247-C6472482379',
729 | merchant_id='A2AYDGTIQUYOHO',
730 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b645234782374903')
731 | parameters = {
732 | 'Action': 'GetCaptureDetails',
733 | 'AmazonCaptureId': 'P01-4763247-C6472482379',
734 | 'SellerId': 'A2AYDGTIQUYOHO',
735 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b645234782374903'}
736 | data_expected = self.request._querystring(parameters)
737 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
738 |
739 | @patch('requests.post')
740 | def test_close_authorization(self, mock_urlopen):
741 | mock_urlopen.side_effect = self.mock_requests_post
742 | self.client.close_authorization(
743 | amazon_authorization_id='P01-4763247-A6568472482379',
744 | closure_reason='testClosure',
745 | merchant_id='A2ATTYIUHBUMTYU',
746 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b645234782374903')
747 | parameters = {
748 | 'Action': 'CloseAuthorization',
749 | 'AmazonAuthorizationId': 'P01-4763247-A6568472482379',
750 | 'ClosureReason': 'testClosure',
751 | 'SellerId': 'A2ATTYIUHBUMTYU',
752 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b645234782374903'}
753 | data_expected = self.request._querystring(parameters)
754 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
755 |
756 | @patch('requests.post')
757 | def test_refund(self, mock_urlopen):
758 | mock_urlopen.side_effect = self.mock_requests_post
759 | self.client.refund(
760 | amazon_capture_id='P01-4763247-C645749',
761 | refund_reference_id='testRefundRefId125',
762 | refund_amount='1',
763 | seller_refund_note='testRefundNote123',
764 | soft_descriptor='testSoftDescriptor167',
765 | merchant_id='A2ATGUHFHWDJEOPW',
766 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b645234782374903')
767 | parameters = {
768 | 'Action': 'Refund',
769 | 'AmazonCaptureId': 'P01-4763247-C645749',
770 | 'RefundReferenceId': 'testRefundRefId125',
771 | 'RefundAmount.Amount': '1',
772 | 'RefundAmount.CurrencyCode': 'USD',
773 | 'SellerRefundNote': 'testRefundNote123',
774 | 'SoftDescriptor': 'testSoftDescriptor167',
775 | 'SellerId': 'A2ATGUHFHWDJEOPW',
776 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b645234782374903'}
777 | data_expected = self.request._querystring(parameters)
778 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
779 |
780 | @patch('requests.post')
781 | def test_get_refund_details(self, mock_urlopen):
782 | mock_urlopen.side_effect = self.mock_requests_post
783 | self.client.get_refund_details(
784 | amazon_refund_id='P01-4763247-R643927483',
785 | merchant_id='A2ATGUYIOUHIJL',
786 | mws_auth_token='amzn.mws.d6ac8f2d-6a5f-b6447623479')
787 | parameters = {
788 | 'Action': 'GetRefundDetails',
789 | 'AmazonRefundId': 'P01-4763247-R643927483',
790 | 'SellerId': 'A2ATGUYIOUHIJL',
791 | 'MWSAuthToken': 'amzn.mws.d6ac8f2d-6a5f-b6447623479'}
792 | data_expected = self.request._querystring(parameters)
793 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
794 |
795 | @patch('requests.post')
796 | def test_get_service_status(self, mock_urlopen):
797 | mock_urlopen.side_effect = self.mock_requests_post
798 | self.client.get_service_status()
799 | parameters = {
800 | 'Action': 'GetServiceStatus'}
801 | data_expected = self.request._querystring(parameters)
802 | self.assertEqual(mock_urlopen.call_args[1]['data'], data_expected)
803 |
804 | def test_is_order_reference_id(self):
805 | self.assertTrue(self.client.is_order_reference_id('P'))
806 | self.assertTrue(self.client.is_order_reference_id('S'))
807 | self.assertFalse(self.client.is_order_reference_id('X'))
808 |
809 | def test_is_billing_agreement_id(self):
810 | self.assertTrue(self.client.is_billing_agreement_id('B'))
811 | self.assertTrue(self.client.is_billing_agreement_id('C'))
812 | self.assertFalse(self.client.is_billing_agreement_id('X'))
813 |
814 | def test_response_invalid_xml(self):
815 | with self.assertRaises(ValueError):
816 | PaymentResponse('')
817 |
818 | @patch('requests.post')
819 | def test_response_to_xml(self, mock_urlopen):
820 | mock_urlopen.side_effect = self.mock_requests_post
821 | response = self.client.get_service_status()
822 | self.assertTrue(et.fromstring(response.to_xml()))
823 |
824 | @patch('requests.post')
825 | def test_response_to_json(self, mock_urlopen):
826 | mock_urlopen.side_effect = self.mock_requests_post
827 | response = self.client.get_service_status()
828 | self.assertTrue(json.loads(response.to_json()))
829 |
830 | def test_response_to_json_utf8(self):
831 | text = self.response.to_json()
832 | utf8_text = '{"test": "الفلانية فلا"}'
833 | self.assertEqual(text, utf8_text)
834 |
835 | @patch('requests.post')
836 | def test_response_to_dict(self, mock_urlopen):
837 | mock_urlopen.side_effect = self.mock_requests_post
838 | response = self.client.get_service_status()
839 | self.assertEqual(type(response.to_dict()), dict)
840 |
841 | @patch('requests.get')
842 | def test_get_login_profile(self, mock_urlopen):
843 | mock_urlopen.side_effect = self.mock_get_login_profile
844 | response = self.client.get_login_profile('access_token', 'client_id')
845 |
846 | def test_environment_variables(self):
847 | os.environ['AP_REGION'] = 'na'
848 | os.environ['AP_MWS_ACCESS_KEY'] = 'AP_MWS_ACCESS_KEY'
849 | os.environ['AP_MERCHANT_ID'] = 'AP_MERCHANT_ID'
850 | os.environ['AP_CURRENCY_CODE'] = 'AP_CURRENCY_CODE'
851 | os.environ['AP_MWS_SECRET_KEY'] = 'AP_MWS_SECRET_KEY'
852 |
853 | client = AmazonPayClient(sandbox=True)
854 | self.assertEqual(client.region, 'na')
855 | self.assertEqual(client.mws_access_key, 'AP_MWS_ACCESS_KEY')
856 | self.assertEqual(client.mws_secret_key, 'AP_MWS_SECRET_KEY')
857 | self.assertEqual(client.merchant_id, 'AP_MERCHANT_ID')
858 | self.assertEqual(client.currency_code, 'AP_CURRENCY_CODE')
859 |
860 | os.environ['AP_REGION'] = 'AP_REGION'
861 | with self.assertRaises(KeyError):
862 | client = AmazonPayClient()
863 |
864 |
865 | if __name__ == "__main__":
866 | unittest.main()
867 |
--------------------------------------------------------------------------------
/test/test_ipn.py:
--------------------------------------------------------------------------------
1 | import os
2 | import unittest
3 | from amazon_pay.ipn_handler import IpnHandler
4 |
5 |
6 | class IpnHandlerTest(unittest.TestCase):
7 |
8 | def setUp(self):
9 | self.maxDiff = None
10 |
11 | with open(
12 | '{}/test.pem'.format(os.path.dirname(os.path.realpath(__file__)))) as pemfile:
13 | self.pem = pemfile.read()
14 |
15 | self.body_valid = b'{\n "Type" : "Notification",\n "MessageId" : "15e7412b-e9ac-5f6a-b6df-0c909df567a0",\n "TopicArn" : "arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU",\n "Message" : "{\\"NotificationReferenceId\\":\\"1111111-1111-11111-1111-11111EXAMPLE\\",\\"MarketplaceID\\":\\"A3BXB0YN3XH17H\\",\\"NotificationType\\":\\"OrderReferenceNotification\\",\\"IsSample\\":true,\\"SellerId\\":\\"AQR8184NJXADU\\",\\"ReleaseEnvironment\\":\\"Sandbox\\",\\"Version\\":\\"2013-01-01\\",\\"NotificationData\\":\\"\\\\n \\\\n \\\\n P01-0000000-0000000-000000<\\\\/AmazonOrderReferenceId>\\\\n \\\\n 0.0<\\\\/Amount>\\\\n USD<\\\\/CurrencyCode>\\\\n <\\\\/OrderTotal>\\\\n \\\\n \\\\n Closed<\\\\/State> \\\\n 2013-01-01T01:01:01.001Z<\\\\/LastUpdateTimestamp>\\\\n AmazonClosed<\\\\/ReasonCode>\\\\n <\\\\/OrderReferenceStatus>\\\\n 2013-01-01T01:01:01.001Z<\\\\/CreationTimestamp> \\\\n 2013-01-01T01:01:01.001Z<\\\\/ExpirationTimestamp>\\\\n <\\\\/OrderReference>\\\\n <\\\\/OrderReferenceNotification>\\",\\"Timestamp\\":\\"2015-04-30T00:06:49.370Z\\"}",\n "Timestamp" : "2015-04-30T00:06:49.434Z",\n "SignatureVersion" : "1",\n "Signature" : "FltJb7WvAGpFayYBgzO5RMd5FoiGizURv+TdPnm/tLXE/E3ndwvLa08hYD3tvmggKSX7Qc0a4mSty9EjZFtTgRVT93jEGuXVBT/WjO5s0lD+7AnuWslxzuVtzLLuMTOnfFUIeoXX2V1bpGwNXPxGfRxLcqz7v41ZdvJvAauoIhjo4oAHF4nZOo2MBd6HY7LMIhJPHS0xmbyQ9Z4QFm5iDaDoSyZ5Q2hCM1RJ1Uv5MQMpNjTXdX4cX81C8lis4nMar/ejDJ8cOwiEweUl5F+y7jxI1uc8AgXNoMGXSwNvdVqoj4zgHVKPkb0Oz7HHY0c4LP9s0FMYkhLBmEGFZVKGKA==",\n "SigningCertURL" : "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem",\n "UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636"\n}'
16 | self.body_invalid = b'{\n "Type" : "Notification",\n "MessageId" : "28908206-3478-5398-bcf7-cfbd41c2a223",\n "TopicArn" : "invalid",\n "Message" : "{\\"NotificationReferenceId\\":\\"1111111-1111-11111-1111-11111EXAMPLE\\",\\"MarketplaceID\\":\\"A3BXB0YN3XH17H\\",\\"NotificationType\\":\\"OrderReferenceNotification\\",\\"IsSample\\":true,\\"SellerId\\":\\"AQR8184NJXADU\\",\\"ReleaseEnvironment\\":\\"Sandbox\\",\\"Version\\":\\"2013-01-01\\",\\"NotificationData\\":\\"\\\\n \\\\n \\\\n P01-0000000-0000000-000000<\\\\/AmazonOrderReferenceId>\\\\n \\\\n 0.0<\\\\/Amount>\\\\n USD<\\\\/CurrencyCode>\\\\n <\\\\/OrderTotal>\\\\n \\\\n \\\\n Closed<\\\\/State> \\\\n 2013-01-01T01:01:01.001Z<\\\\/LastUpdateTimestamp>\\\\n AmazonClosed<\\\\/ReasonCode>\\\\n <\\\\/OrderReferenceStatus>\\\\n 2013-01-01T01:01:01.001Z<\\\\/CreationTimestamp> \\\\n 2013-01-01T01:01:01.001Z<\\\\/ExpirationTimestamp>\\\\n <\\\\/OrderReference>\\\\n <\\\\/OrderReferenceNotification>\\",\\"Timestamp\\":\\"2015-04-30T00:12:42.805Z\\"}",\n "Timestamp" : "2015-04-30T00:12:42.885Z",\n "SignatureVersion" : "1",\n "Signature" : "ZChg+1FlUr8OUfu9kd7B2wzT7G1Z0BWf2mH3MH5MtDqhI4t9j5lvG9YqC20LSXV+x3ajvnEmyt2YO635KIAA+Ig4IKeCgnm/YJNjxqtdaOS01M4+3vw9zaeKPY3FlTBgG3T+J3+K3SLARIeblVJhabA0TXVatqtFbMwV81xxKnLxqE5Ik8MZSBAQdHFm6u2lNIruluQakL1mmDUm/2Szj+DkMFrjsQce7fcbkr5TCJ0YB5oYAtkG2MKODYEXYAAlpUe3G0qtBT8WyOVkMGyVQswpgZbJseCER/5xU1Vjm7UNL+tR5AbOABDX/4wi+5670gqmEumny6CvZTxIVbLmjg==",\n "SigningCertURL" : "https://invalid.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem",\n "UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636"\n}'
17 |
18 | self.headers = {
19 | 'Content-Type': 'text/plain; charset=UTF-8',
20 | 'Accept-Encoding': 'gzip,deflate',
21 | 'Host': 'test.me',
22 | 'X-Amz-Sns-Message-Id': '15e7412b-e9ac-5f6a-b6df-0c909df567a0',
23 | 'Connection': 'Keep-Alive',
24 | 'User-Agent': 'Amazon Simple Notification Service Agent',
25 | 'X-Amz-Sns-Message-Type': 'Notification',
26 | 'X-Amz-Sns-Topic-Arn': 'arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU',
27 | 'Content-Length': '100',
28 | 'X-Amz-Sns-Subscription-Arn': 'arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636'}
29 |
30 | self.ipn_handler = IpnHandler(
31 | body=self.body_valid,
32 | headers=self.headers)
33 |
34 | def test_validate_header(self):
35 | self.assertTrue(self.ipn_handler._validate_header())
36 |
37 | with self.assertRaises(ValueError):
38 | ipn_handler = IpnHandler(
39 | body=self.body_invalid,
40 | headers={'test': 'test'})
41 | ipn_handler._validate_header()
42 |
43 | with self.assertRaises(ValueError):
44 | ipn_handler = IpnHandler(
45 | body=self.body_valid,
46 | headers={'X-Amz-Sns-Topic-Arn': 'invalid'})
47 | ipn_handler._validate_header()
48 |
49 | def test_validate_cert_url(self):
50 | with self.assertRaises(ValueError):
51 | ipn_handler = IpnHandler(
52 | body=self.body_invalid,
53 | headers={'test': 'test'})
54 | ipn_handler._validate_cert_url()
55 |
56 | # https://sns.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem
57 | good = b'{\n "Type" : "Notification",\n "MessageId" : "15e7412b-e9ac-5f6a-b6df-0c909df567a0",\n "TopicArn" : "arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU",\n "Message" : "{\\"NotificationReferenceId\\":\\"1111111-1111-11111-1111-11111EXAMPLE\\",\\"MarketplaceID\\":\\"A3BXB0YN3XH17H\\",\\"NotificationType\\":\\"OrderReferenceNotification\\",\\"IsSample\\":true,\\"SellerId\\":\\"AQR8184NJXADU\\",\\"ReleaseEnvironment\\":\\"Sandbox\\",\\"Version\\":\\"2013-01-01\\",\\"NotificationData\\":\\"\\\\n \\\\n \\\\n P01-0000000-0000000-000000<\\\\/AmazonOrderReferenceId>\\\\n \\\\n 0.0<\\\\/Amount>\\\\n USD<\\\\/CurrencyCode>\\\\n <\\\\/OrderTotal>\\\\n \\\\n \\\\n Closed<\\\\/State> \\\\n 2013-01-01T01:01:01.001Z<\\\\/LastUpdateTimestamp>\\\\n AmazonClosed<\\\\/ReasonCode>\\\\n <\\\\/OrderReferenceStatus>\\\\n 2013-01-01T01:01:01.001Z<\\\\/CreationTimestamp> \\\\n 2013-01-01T01:01:01.001Z<\\\\/ExpirationTimestamp>\\\\n <\\\\/OrderReference>\\\\n <\\\\/OrderReferenceNotification>\\",\\"Timestamp\\":\\"2015-04-30T00:06:49.370Z\\"}",\n "Timestamp" : "2015-04-30T00:06:49.434Z",\n "SignatureVersion" : "1",\n "Signature" : "FltJb7WvAGpFayYBgzO5RMd5FoiGizURv+TdPnm/tLXE/E3ndwvLa08hYD3tvmggKSX7Qc0a4mSty9EjZFtTgRVT93jEGuXVBT/WjO5s0lD+7AnuWslxzuVtzLLuMTOnfFUIeoXX2V1bpGwNXPxGfRxLcqz7v41ZdvJvAauoIhjo4oAHF4nZOo2MBd6HY7LMIhJPHS0xmbyQ9Z4QFm5iDaDoSyZ5Q2hCM1RJ1Uv5MQMpNjTXdX4cX81C8lis4nMar/ejDJ8cOwiEweUl5F+y7jxI1uc8AgXNoMGXSwNvdVqoj4zgHVKPkb0Oz7HHY0c4LP9s0FMYkhLBmEGFZVKGKA==",\n "SigningCertURL" : "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem",\n "UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636"\n}'
58 | ipn_handler = IpnHandler(
59 | body=good,
60 | headers={'test': 'test'})
61 | self.assertTrue(ipn_handler._validate_cert_url())
62 |
63 | # http://invalid.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem
64 | bad = b'{\n "Type" : "Notification",\n "MessageId" : "28908206-3478-5398-bcf7-cfbd41c2a223",\n "TopicArn" : "invalid",\n "Message" : "{\\"NotificationReferenceId\\":\\"1111111-1111-11111-1111-11111EXAMPLE\\",\\"MarketplaceID\\":\\"A3BXB0YN3XH17H\\",\\"NotificationType\\":\\"OrderReferenceNotification\\",\\"IsSample\\":true,\\"SellerId\\":\\"AQR8184NJXADU\\",\\"ReleaseEnvironment\\":\\"Sandbox\\",\\"Version\\":\\"2013-01-01\\",\\"NotificationData\\":\\"\\\\n \\\\n \\\\n P01-0000000-0000000-000000<\\\\/AmazonOrderReferenceId>\\\\n \\\\n 0.0<\\\\/Amount>\\\\n USD<\\\\/CurrencyCode>\\\\n <\\\\/OrderTotal>\\\\n \\\\n \\\\n Closed<\\\\/State> \\\\n 2013-01-01T01:01:01.001Z<\\\\/LastUpdateTimestamp>\\\\n AmazonClosed<\\\\/ReasonCode>\\\\n <\\\\/OrderReferenceStatus>\\\\n 2013-01-01T01:01:01.001Z<\\\\/CreationTimestamp> \\\\n 2013-01-01T01:01:01.001Z<\\\\/ExpirationTimestamp>\\\\n <\\\\/OrderReference>\\\\n <\\\\/OrderReferenceNotification>\\",\\"Timestamp\\":\\"2015-04-30T00:12:42.805Z\\"}",\n "Timestamp" : "2015-04-30T00:12:42.885Z",\n "SignatureVersion" : "1",\n "Signature" : "ZChg+1FlUr8OUfu9kd7B2wzT7G1Z0BWf2mH3MH5MtDqhI4t9j5lvG9YqC20LSXV+x3ajvnEmyt2YO635KIAA+Ig4IKeCgnm/YJNjxqtdaOS01M4+3vw9zaeKPY3FlTBgG3T+J3+K3SLARIeblVJhabA0TXVatqtFbMwV81xxKnLxqE5Ik8MZSBAQdHFm6u2lNIruluQakL1mmDUm/2Szj+DkMFrjsQce7fcbkr5TCJ0YB5oYAtkG2MKODYEXYAAlpUe3G0qtBT8WyOVkMGyVQswpgZbJseCER/5xU1Vjm7UNL+tR5AbOABDX/4wi+5670gqmEumny6CvZTxIVbLmjg==",\n "SigningCertURL" : "http://invalid.us-east-1.amazonaws.com/SimpleNotificationService-d6d679a1d18e95c2f9ffcf11f4f9e198.pem",\n "UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636"\n}'
65 | ipn_handler = IpnHandler(
66 | body=bad,
67 | headers={'test': 'test'})
68 | with self.assertRaises(ValueError):
69 | ipn_handler._validate_cert_url()
70 |
71 | # https://https://sns.foo.amazon.com/large.file?file=ddos.pem
72 | bad = b'{\n "Type" : "Notification",\n "MessageId" : "28908206-3478-5398-bcf7-cfbd41c2a223",\n "TopicArn" : "invalid",\n "Message" : "{\\"NotificationReferenceId\\":\\"1111111-1111-11111-1111-11111EXAMPLE\\",\\"MarketplaceID\\":\\"A3BXB0YN3XH17H\\",\\"NotificationType\\":\\"OrderReferenceNotification\\",\\"IsSample\\":true,\\"SellerId\\":\\"AQR8184NJXADU\\",\\"ReleaseEnvironment\\":\\"Sandbox\\",\\"Version\\":\\"2013-01-01\\",\\"NotificationData\\":\\"\\\\n \\\\n \\\\n P01-0000000-0000000-000000<\\\\/AmazonOrderReferenceId>\\\\n \\\\n 0.0<\\\\/Amount>\\\\n USD<\\\\/CurrencyCode>\\\\n <\\\\/OrderTotal>\\\\n \\\\n \\\\n Closed<\\\\/State> \\\\n 2013-01-01T01:01:01.001Z<\\\\/LastUpdateTimestamp>\\\\n AmazonClosed<\\\\/ReasonCode>\\\\n <\\\\/OrderReferenceStatus>\\\\n 2013-01-01T01:01:01.001Z<\\\\/CreationTimestamp> \\\\n 2013-01-01T01:01:01.001Z<\\\\/ExpirationTimestamp>\\\\n <\\\\/OrderReference>\\\\n <\\\\/OrderReferenceNotification>\\",\\"Timestamp\\":\\"2015-04-30T00:12:42.805Z\\"}",\n "Timestamp" : "2015-04-30T00:12:42.885Z",\n "SignatureVersion" : "1",\n "Signature" : "ZChg+1FlUr8OUfu9kd7B2wzT7G1Z0BWf2mH3MH5MtDqhI4t9j5lvG9YqC20LSXV+x3ajvnEmyt2YO635KIAA+Ig4IKeCgnm/YJNjxqtdaOS01M4+3vw9zaeKPY3FlTBgG3T+J3+K3SLARIeblVJhabA0TXVatqtFbMwV81xxKnLxqE5Ik8MZSBAQdHFm6u2lNIruluQakL1mmDUm/2Szj+DkMFrjsQce7fcbkr5TCJ0YB5oYAtkG2MKODYEXYAAlpUe3G0qtBT8WyOVkMGyVQswpgZbJseCER/5xU1Vjm7UNL+tR5AbOABDX/4wi+5670gqmEumny6CvZTxIVbLmjg==",\n "SigningCertURL" : "https://sns.foo.amazon.com/large.file?file=ddos.pem",\n "UnsubscribeURL" : "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:291180941288:A3BXB0YN3XH17HAQR8184NJXADU:6cab6de5-c2c7-4ef0-9d4f-d6a5db8b1636"\n}'
73 | ipn_handler = IpnHandler(
74 | body=bad,
75 | headers={'test': 'test'})
76 | with self.assertRaises(ValueError):
77 | ipn_handler._validate_cert_url()
78 |
79 | def test_validate_signature(self):
80 | self.ipn_handler._pem = self.pem
81 | self.ipn_handler._validate_signature()
82 |
83 | with self.assertRaises(ValueError):
84 | ipn_handler = IpnHandler(
85 | body=self.body_invalid,
86 | headers=self.headers)
87 | ipn_handler._pem = self.pem
88 | ipn_handler._validate_signature()
89 |
--------------------------------------------------------------------------------
/test/test_lwa.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from unittest.mock import Mock, patch
3 | from amazon_pay.login_with_amazon import LoginWithAmazon
4 |
5 |
6 | class LoginWithAmazonClientTest(unittest.TestCase):
7 |
8 | def setUp(self):
9 | self.maxDiff = None
10 |
11 | self.lwa_client = LoginWithAmazon(
12 | client_id='client_id',
13 | region='na',
14 | sandbox=True)
15 |
16 | def mock_get_error(self, url, headers, params, verify):
17 | mock_response = Mock()
18 | mock_response.json.return_value = {
19 | "error": "test error",
20 | "error_description": "This is a test error"}
21 | mock_response.status_code = 200
22 | return mock_response
23 |
24 | def mock_get_error_aud(self, url, headers, params, verify):
25 | mock_response = Mock()
26 | mock_response.json.return_value = {"test": "aud not present"}
27 | mock_response.status_code = 200
28 | return mock_response
29 |
30 | def mock_get_success(self, url, headers, params, verify):
31 | mock_response = Mock()
32 | mock_response.json.return_value = {"aud": "client_id"}
33 | mock_response.status_code = 200
34 | return mock_response
35 |
36 | @patch('requests.get')
37 | def test_get_login_profile_error(self, mock_urlopen):
38 | mock_urlopen.side_effect = self.mock_get_error
39 | with self.assertRaises(ValueError):
40 | self.lwa_client.get_login_profile(access_token='access_token')
41 |
42 | @patch('requests.get')
43 | def test_get_login_profile_error_aud(self, mock_urlopen):
44 | mock_urlopen.side_effect = self.mock_get_error_aud
45 | with self.assertRaises(ValueError):
46 | self.lwa_client.get_login_profile(access_token='access_token')
47 |
48 | @patch('requests.get')
49 | def test_get_login_profile_success(self, mock_urlopen):
50 | mock_urlopen.side_effect = self.mock_get_success
51 | res = self.lwa_client.get_login_profile(
52 | access_token='access_token')
53 | print(res)
54 |
55 | def test_invalid_region(self):
56 | with self.assertRaises(KeyError):
57 | LoginWithAmazon(client_id='test', region='xx', sandbox=True)
58 |
--------------------------------------------------------------------------------