Created
March 5, 2025 15:19
-
-
Save urielhdz/057ae041a2f1f4f27f7521250d0bb401 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
├── .github | |
└── ISSUE_TEMPLATE.md | |
├── .gitignore | |
├── .releasinator.rb | |
├── .rspec | |
├── .travis.yml | |
├── CHANGELOG.md | |
├── CONTRIBUTING.md | |
├── Gemfile | |
├── LICENSE | |
├── README.md | |
├── Rakefile | |
├── data | |
├── DigiCertHighAssuranceEVRootCA.pem | |
├── DigiCertSHA2ExtendedValidationServerCA.pem | |
└── paypal.crt | |
├── lib | |
├── generators | |
│ └── paypal | |
│ │ └── sdk | |
│ │ ├── USAGE | |
│ │ ├── install_generator.rb | |
│ │ └── templates | |
│ │ ├── paypal.rb | |
│ │ └── paypal.yml | |
├── paypal-sdk-core.rb | |
├── paypal-sdk-rest.rb | |
└── paypal-sdk | |
│ ├── core | |
│ ├── api.rb | |
│ ├── api | |
│ │ ├── base.rb | |
│ │ ├── data_types | |
│ │ │ ├── array_with_block.rb | |
│ │ │ ├── base.rb | |
│ │ │ ├── enum.rb | |
│ │ │ └── simple_types.rb | |
│ │ ├── ipn.rb | |
│ │ └── rest.rb | |
│ ├── authentication.rb | |
│ ├── config.rb | |
│ ├── credential.rb | |
│ ├── credential | |
│ │ ├── base.rb | |
│ │ ├── certificate.rb | |
│ │ ├── signature.rb | |
│ │ └── third_party | |
│ │ │ ├── subject.rb | |
│ │ │ └── token.rb | |
│ ├── exceptions.rb | |
│ ├── logging.rb | |
│ ├── openid_connect.rb | |
│ ├── openid_connect | |
│ │ ├── api.rb | |
│ │ ├── data_types.rb | |
│ │ ├── get_api.rb | |
│ │ ├── request_data_type.rb | |
│ │ └── set_api.rb | |
│ ├── util.rb | |
│ └── util | |
│ │ ├── http_helper.rb | |
│ │ ├── oauth_signature.rb | |
│ │ └── ordered_hash.rb | |
│ ├── rest.rb | |
│ └── rest | |
│ ├── api.rb | |
│ ├── data_types.rb | |
│ ├── error_hash.rb | |
│ ├── get_api.rb | |
│ ├── request_data_type.rb | |
│ ├── set_api.rb | |
│ └── version.rb | |
├── paypal-sdk-rest.gemspec | |
├── samples | |
├── Gemfile | |
├── README.md | |
├── app.rb | |
├── authorization | |
│ ├── capture.rb | |
│ ├── find.rb | |
│ ├── reauthorize.rb | |
│ └── void.rb | |
├── capture | |
│ ├── find.rb | |
│ └── refund.rb | |
├── config.ru | |
├── config | |
│ └── paypal.yml | |
├── invoice | |
│ ├── cancel.rb | |
│ ├── create.rb | |
│ ├── get.rb | |
│ ├── get_all.rb | |
│ ├── remind.rb | |
│ ├── send_invoice.rb | |
│ └── third_party_invoice.rb | |
├── invoice_template | |
│ ├── create.rb | |
│ ├── delete.rb | |
│ ├── get.rb | |
│ ├── get_all.rb | |
│ └── update.rb | |
├── notifications | |
│ ├── create.rb | |
│ ├── delete_webhook.rb | |
│ ├── get_all_webhooks.rb | |
│ ├── get_subscribed_webhooks_event_types.rb | |
│ ├── get_webhook.rb | |
│ ├── get_webhook_event.rb | |
│ ├── get_webhooks_event_types.rb | |
│ ├── resend_webhook_event.rb | |
│ ├── search_webhook_event.rb | |
│ ├── simulate_event.rb | |
│ ├── update_webhook.rb | |
│ └── verify_webhook_event.rb | |
├── payment | |
│ ├── all.rb | |
│ ├── create_future_payment.rb | |
│ ├── create_third_party_with_paypal.rb | |
│ ├── create_with_paypal.rb | |
│ ├── execute.rb | |
│ ├── find.rb | |
│ └── update.rb | |
├── payouts | |
│ ├── cancel.rb | |
│ ├── create.rb | |
│ ├── createSync.rb | |
│ ├── createVenmo.rb | |
│ ├── get_batch_status.rb | |
│ └── get_item_status.rb | |
├── public | |
│ └── images | |
│ │ ├── edt-format-source-button.png | |
│ │ └── play_button.png | |
├── runner.rb | |
├── sale | |
│ ├── find.rb | |
│ └── refund.rb | |
└── views | |
│ ├── display_hash.haml | |
│ └── index.haml | |
└── spec | |
├── README.md | |
├── config | |
├── cacert.pem | |
├── cert_key.pem | |
├── paypal.yml | |
└── sample_data.yml | |
├── core | |
├── api | |
│ ├── data_type_spec.rb | |
│ └── rest_spec.rb | |
├── config_spec.rb | |
├── logging_spec.rb | |
└── openid_connect_spec.rb | |
├── invoice_examples_spec.rb | |
├── log | |
└── .gitkeep | |
├── payments_examples_spec.rb | |
├── payouts_examples_spec.rb | |
├── rest | |
├── data_types_spec.rb | |
└── error_hash_spec.rb | |
├── spec_helper.rb | |
├── subscription_examples_spec.rb | |
├── support | |
└── sample_data.rb | |
├── web_profile_examples_spec.rb | |
└── webhooks_examples_spec.rb | |
/.github/ISSUE_TEMPLATE.md: | |
-------------------------------------------------------------------------------- | |
1 | <!-- ** General notice: if you have an API issue, see our [REST API issues](https://github.com/paypal/PayPal-REST-API-issues) repository, or contact [PayPal Tech Support](https://developer.paypal.com/support/). ** --> | |
2 | ### General information | |
3 | | |
4 | * SDK/Library version: <!-- Example: 4.7.2 --> | |
5 | * Environment: <!-- Is this issue in Sandbox or Production? --> | |
6 | * `PayPal-Debug-ID` values: <!-- Report PayPal-Debug-IDs from any logs --> | |
7 | * Language, language version, and OS: <!-- Example: Java 1.8.0_101-b13 on Ubuntu 16.10 --> | |
8 | | |
9 | ### Issue description | |
10 | | |
11 | <!-- To help us quickly reproduce your issue, include as many details as possible, such as logs, steps to reproduce, and so on. If the issue reports a new feature, follow the [user story](https://en.wikipedia.org/wiki/User_story) format to clearly describe the use case. --> | |
12 | | |
-------------------------------------------------------------------------------- | |
/.gitignore: | |
-------------------------------------------------------------------------------- | |
1 | .DS_Store | |
2 | *.gem | |
3 | *.rbc | |
4 | *.log | |
5 | *.swp | |
6 | .bundle | |
7 | .config | |
8 | .yardoc | |
9 | samples/Gemfile.lock | |
10 | InstalledFiles | |
11 | _yardoc | |
12 | coverage | |
13 | doc/ | |
14 | lib/bundler/man | |
15 | pkg | |
16 | rdoc | |
17 | spec/reports | |
18 | test/tmp | |
19 | test/version_tmp | |
20 | tmp | |
21 | .idea | |
22 | Gemfile.lock | |
23 | | |
-------------------------------------------------------------------------------- | |
/.releasinator.rb: | |
-------------------------------------------------------------------------------- | |
1 | #### releasinator config #### | |
2 | configatron.product_name = "PayPal Ruby SDK" | |
3 | | |
4 | # List of items to confirm from the person releasing. Required, but empty list is ok. | |
5 | configatron.prerelease_checklist_items = [ | |
6 | "Sanity check the master branch." | |
7 | ] | |
8 | | |
9 | def validate_version_match() | |
10 | if 'v' + package_version() != @current_release.version | |
11 | Printer.fail("Package.json version #{package_version} does not match changelog version #{@current_release.version}.") | |
12 | abort() | |
13 | end | |
14 | Printer.success("Package.json version #{package_version} matches latest changelog version #{@current_release.version}.") | |
15 | end | |
16 | | |
17 | def validate_paths | |
18 | @validator.validate_in_path("jq") | |
19 | end | |
20 | | |
21 | configatron.custom_validation_methods = [ | |
22 | method(:validate_paths), | |
23 | method(:validate_version_match) | |
24 | ] | |
25 | | |
26 | def build_method | |
27 | CommandProcessor.command("bundle exec rspec", live_output=true) | |
28 | # at present functional tests are failing , so we are skipping functional test execution temporally | |
29 | #CommandProcessor.command("bundle exec rspec --tag integration", live_output=true) | |
30 | Rake::Task["build"].invoke | |
31 | Rake::Task["release:guard_clean"].invoke | |
32 | end | |
33 | | |
34 | # The command that builds the sdk. Required. | |
35 | configatron.build_method = method(:build_method) | |
36 | | |
37 | def publish_to_package_manager(version) | |
38 | Rake::Task["release:rubygem_push"].invoke | |
39 | end | |
40 | | |
41 | # The method that publishes the sdk to the package manager. Required. | |
42 | configatron.publish_to_package_manager_method = method(:publish_to_package_manager) | |
43 | | |
44 | | |
45 | def wait_for_package_manager(version) | |
46 | CommandProcessor.wait_for("wget -U \"non-empty-user-agent\" -qO- https://rubygems.org/gems/paypal-sdk-rest/versions/#{package_version} | cat") | |
47 | end | |
48 | | |
49 | # The method that waits for the package manager to be done. Required | |
50 | configatron.wait_for_package_manager_method = method(:wait_for_package_manager) | |
51 | | |
52 | # Whether to publish the root repo to GitHub. Required. | |
53 | configatron.release_to_github = true | |
54 | | |
55 | def package_version() | |
56 | File.open("lib/paypal-sdk/rest/version.rb", 'r') do |f| | |
57 | f.each_line do |line| | |
58 | if line.match (/VERSION = \"\d*\.\d*\.\d*\"/) | |
59 | return line.strip.split('=')[1].strip.split('"')[1] | |
60 | end | |
61 | end | |
62 | end | |
63 | end | |
64 | | |
-------------------------------------------------------------------------------- | |
/.rspec: | |
-------------------------------------------------------------------------------- | |
1 | --color | |
2 | --require spec_helper | |
3 | --tty | |
4 | --format documentation | |
5 | | |
-------------------------------------------------------------------------------- | |
/.travis.yml: | |
-------------------------------------------------------------------------------- | |
1 | sudo: required | |
2 | dist: trusty | |
3 | language: ruby | |
4 | rvm: | |
5 | - "2.0.0" | |
6 | - "2.2.5" | |
7 | - "2.3.1" | |
8 | script: "bundle exec rspec" | |
9 | | |
10 | sudo: false | |
11 | | |
12 | notifications: | |
13 | recipients: | |
14 | - [email protected] | |
15 | | |
-------------------------------------------------------------------------------- | |
/CHANGELOG.md: | |
-------------------------------------------------------------------------------- | |
1 | PayPal Ruby SDK release notes | |
2 | ============================= | |
3 | | |
4 | v1.7.4 | |
5 | ------ | |
6 | * Update Payouts API for latest schema [#388](https://github.com/paypal/PayPal-Ruby-SDK/pull/388). | |
7 | | |
8 | v1.7.3 | |
9 | ------ | |
10 | * Adding disputes in data_types.rb. | |
11 | * Fix issue `no implicit conversion of Array into String` [#363](https://github.com/paypal/PayPal-Ruby-SDK/pull/363). | |
12 | | |
13 | v1.7.2 | |
14 | ------ | |
15 | * Remove logging of unknown fields. | |
16 | * Fix issue with `require net/http` [#331](https://github.com/paypal/PayPal-Ruby-SDK/pull/331). Thanks Borzik. | |
17 | | |
18 | v1.7.1 | |
19 | ------ | |
20 | * Use `NET` library to fetch webhook ceritifcate. Thanks Kramer. | |
21 | | |
22 | v1.7.0 | |
23 | ------ | |
24 | * Add `invoice_address` field on Invoice's BillingInfo and ShippingInfo types [#322](https://github.com/paypal/PayPal-Ruby-SDK/pull/322) and [#326](https://github.com/paypal/PayPal-Ruby-SDK/pull/326). | |
25 | * Fix issue with capitalization for encType in Links attribute [#314](https://github.com/paypal/PayPal-Ruby-SDK/issues/314). | |
26 | | |
27 | v1.6.1 | |
28 | ------ | |
29 | * Fix issue with wrong method declared for `WebhookEvent.find` and `WebHookEvent.all` [#270](https://github.com/paypal/PayPal-Ruby-SDK/pull/270) and [#306](https://github.com/paypal/PayPal-Ruby-SDK/pull/306). | |
30 | * Fix issue with invalid JSON handling. [#302](https://github.com/paypal/PayPal-Ruby-SDK/issues/302). | |
31 | * Fix issue with missing `ResourceInvalid` type [#298](https://github.com/paypal/PayPal-Ruby-SDK/issues/298). | |
32 | * Fix issue with `ErrorHash.convert` for nested hashes [#257](https://github.com/paypal/PayPal-Ruby-SDK/issues/257). | |
33 | * Update code to find PayPal approval redirect URL using relationship identifier instead of by method. | |
34 | | |
35 | v1.6.0 | |
36 | ------ | |
37 | * Update Payments API for latest schema [#246](https://github.com/paypal/PayPal-Ruby-SDK/pull/246). | |
38 | * Changed no method found log to debug [#245](https://github.com/paypal/PayPal-Ruby-SDK/pull/245). | |
39 | | |
40 | v1.5.0 | |
41 | ------ | |
42 | * Update Payments Experience API for latest schema [#242](https://github.com/paypal/PayPal-Ruby-SDK/pull/242). | |
43 | * Update Webhooks for latest schema [#238](https://github.com/paypal/PayPal-Ruby-SDK/pull/238). | |
44 | * Flatten error hashes [#240](https://github.com/paypal/PayPal-Ruby-SDK/pull/240). | |
45 | * Use SecureRandom instead of uuidtools [#237](https://github.com/paypal/PayPal-Ruby-SDK/pull/237). | |
46 | * Update Invoicing Templates for latest schema [#235](https://github.com/paypal/PayPal-Ruby-SDK/pull/235). | |
47 | * Update OpenID Connect signin URL [#225](https://github.com/paypal/PayPal-Ruby-SDK/pull/225). | |
48 | | |
49 | v1.4.9 | |
50 | ------ | |
51 | * Use String.force_encoding to force conversion to UTF-8 [#220](https://github.com/paypal/PayPal-Ruby-SDK/pull/220). | |
52 | * Fix WebProfile GET/retrieve to return a WebProfile instance [#219](https://github.com/paypal/PayPal-Ruby-SDK/pull/219). | |
53 | | |
54 | v1.4.8 | |
55 | ------ | |
56 | * Added (optional) exception raising on API errors [#216](https://github.com/paypal/PayPal-Ruby-SDK/pull/216). | |
57 | * Use UTF-8 as the character set when generating CRC32 checksum when validating webhook events [#215](https://github.com/paypal/PayPal-Ruby-SDK/pull/215). | |
58 | * Added Payment convenience methods [#212](https://github.com/paypal/PayPal-Ruby-SDK/pull/212). | |
59 | | |
60 | v1.4.7 | |
61 | ------ | |
62 | * Enabled third party invoicing for all invoicing API operations [#209](https://github.com/paypal/PayPal-Ruby-SDK/pull/209). | |
63 | | |
64 | v1.4.6 | |
65 | ------ | |
66 | * Enabled Third Party Invoicing [#204](https://github.com/paypal/PayPal-Ruby-SDK/pull/204). | |
67 | * Enable Passing Custom Headers [#197](https://github.com/paypal/PayPal-Ruby-SDK/pull/197). | |
68 | | |
69 | v1.4.5 | |
70 | ------ | |
71 | * Log error responses in live mode [#192](https://github.com/paypal/PayPal-Ruby-SDK/pull/192). | |
72 | * Fixed patch_requests by Array in update method of CreditCard [#193](https://github.com/paypal/PayPal-Ruby-SDK/pull/193). | |
73 | | |
74 | v1.4.4 | |
75 | ------ | |
76 | * Update on Invoicing API changes [#189](https://github.com/paypal/PayPal-Ruby-SDK/pull/189). | |
77 | | |
78 | v1.4.3 | |
79 | ------ | |
80 | * Fix issue where uninitialized constant PayPal::SDK::Core::API::Merchant occurs for merchant-sdk-ruby issue [#184](https://github.com/paypal/PayPal-Ruby-SDK/issues/184). | |
81 | | |
82 | v1.4.2 | |
83 | ------ | |
84 | * Fix test category [#178](https://github.com/paypal/PayPal-Ruby-SDK/issues/178). | |
85 | * Delete code irrelevant to REST APIs [#179](https://github.com/paypal/PayPal-Ruby-SDK/issues/179). | |
86 | * Fix OpenSSL::X509::StoreError: system lib error for webhook validation [#170](https://github.com/paypal/PayPal-Ruby-SDK/issues/170). | |
87 | * Fix incorrect warning message when using DEBUG logging on live [#182](https://github.com/paypal/PayPal-Ruby-SDK/pull/182). | |
88 | | |
89 | v1.4.1 | |
90 | ------ | |
91 | * Fix Webhook common name verification. | |
92 | | |
93 | v1.4.0 | |
94 | ------ | |
95 | * Fix CreditCard.update(). | |
96 | * Payment API support. | |
97 | * Updated TLS warning message. | |
98 | | |
99 | v1.3.4 | |
100 | ------ | |
101 | * Fix payment.update() [#163](https://github.com/paypal/PayPal-Ruby-SDK/issues/163). | |
102 | * Include openssl version in user-agent header. | |
103 | * Add TLS v1.2 support. | |
104 | | |
105 | v1.3.3 | |
106 | ------ | |
107 | * Added failover for capturing debug ID. | |
108 | * Enabled verbose payload logging [#146](https://github.com/paypal/PayPal-Ruby-SDK/issues/146). | |
109 | * Removed `time_updated` field in agreement_transaction per [API change](https://developer.paypal.com/webapps/developer/docs/api/#agreementtransaction-object). | |
110 | * Removed `payment_details` field in invoice per [API change](https://developer.paypal.com/webapps/developer/docs/api/#invoice-object). | |
111 | * Added `payment_options` field to Transaction per [API change](https://developer.paypal.com/webapps/developer/docs/api/#transaction-object). | |
112 | * Added secure logging to avoid logging confidential data (e.g., credit card number). | |
113 | | |
114 | v1.3.2 | |
115 | ------ | |
116 | * Fixed webprofile.create(). | |
117 | * Fixed webprofile.get_list(). | |
118 | * Updated webprofile test cases. | |
119 | | |
120 | v1.3.1 | |
121 | ------ | |
122 | * Added CreditCard list() support. | |
123 | * Added request/response body debugging statements. | |
124 | * Fixed future payment support, moved sample code, and added docs [#137](https://github.com/paypal/PayPal-Ruby-SDK/issues/137). | |
125 | | |
126 | v1.3.0 - June 30, 2015 | |
127 | ---------------------- | |
128 | * Added Webhook validation. | |
129 | | |
130 | v1.2.2 - June 17, 2015 | |
131 | --------------------- | |
132 | * Fixed NameError due to underscore in variable name. | |
133 | * Fixed Vault endpoints. | |
134 | | |
135 | v1.2.1 - May 21, 2015 | |
136 | -------------------- | |
137 | * Paypal-Debug-Id printed for any exception. | |
138 | | |
139 | v1.2.0 - March 3, 2015 | |
140 | ---------------------- | |
141 | * Webhook management API support added. | |
142 | | |
143 | v1.1.2 - March 3, 2015 | |
144 | ---------------------- | |
145 | * Updated payment data models. | |
146 | | |
147 | v1.1.1 - February 5, 2015 | |
148 | ------------------------- | |
149 | * Packaged paypal cert in gem. | |
150 | | |
151 | v1.1.0 - February 4, 2015 | |
152 | ------------------------ | |
153 | * Added Payouts support. | |
154 | * Improved sample page layout. | |
155 | | |
156 | v1.0.0 - January 27, 2015 | |
157 | ----------------------- | |
158 | * Merged sdk-core-ruby with paypal-ruby-sdk. | |
159 | | |
160 | v0.10.0 - January 7, 2015 | |
161 | ------------------------ | |
162 | * Added subscription (billing plan and agreement) support. | |
163 | | |
164 | v0.9.1 - December 19, 2014 | |
165 | ------------------------- | |
166 | * Separated out extended data types (future payment). | |
167 | | |
168 | v0.9.0 - December 15, 2014 | |
169 | ------------------------- | |
170 | * Added payment-experience (web profiles) support. | |
171 | * Added test execution guide. | |
172 | | |
173 | v0.8.0 - December 11, 2014 | |
174 | ------------------------- | |
175 | * Added order/auth/capture support. | |
176 | * Grouped tests in 2 categories: unit tests, integration(functional) tests. | |
177 | * Disabled some tests that involve manual steps (e.g., log in to PayPal website). | |
178 | | |
179 | v0.7.3 - November 21, 2014 | |
180 | ------------------------- | |
181 | * Changed Correlation ID header for future payment. | |
182 | * Added data model for Order/Auth/Capture. | |
183 | | |
184 | v0.7.2 - October 20, 2014 | |
185 | ------------------------ | |
186 | * Added Order support. | |
187 | | |
188 | v0.7.1 - October 8, 2014 | |
189 | ----------------------- | |
190 | * Added Auth support. | |
191 | | |
192 | v0.7.0 - July 1, 2014 | |
193 | -------------------- | |
194 | * Added future payment support. | |
195 | | |
196 | v0.6.1 - Apr 04, 2014 | |
197 | -------------------- | |
198 | * Added support for Invoice APIs. | |
199 | | |
200 | v0.6.0 - May 30, 2013 | |
201 | -------------------- | |
202 | * Added support for Auth and Capture APIs. | |
203 | | |
204 | v0.5.1 - Apr 26, 2013 | |
205 | -------------------- | |
206 | * Update core version to 0.2.3 for OpenID Connect. | |
207 | | |
208 | v0.5.0 - Mar 07, 2013 | |
209 | -------------------- | |
210 | * Initial Release. | |
211 | | |
-------------------------------------------------------------------------------- | |
/CONTRIBUTING.md: | |
-------------------------------------------------------------------------------- | |
1 | # Contribute to the PayPal Ruby SDK | |
2 | | |
3 | ### *Pull requests are welcome!* | |
4 | | |
5 | | |
6 | General Guidelines | |
7 | ------------------ | |
8 | | |
9 | * **Code style.** Please follow local code style. Ask if you're unsure. | |
10 | * **No warnings.** All generated code must compile without warnings. | |
11 | | |
-------------------------------------------------------------------------------- | |
/Gemfile: | |
-------------------------------------------------------------------------------- | |
1 | source 'https://rubygems.org' | |
2 | | |
3 | gemspec | |
4 | | |
5 | #gem 'paypal-sdk-core', :git => "https://github.com/paypal/sdk-core-ruby.git" | |
6 | | |
7 | gem 'rake', :require => false | |
8 | | |
9 | group :test do | |
10 | gem 'simplecov', :require => false | |
11 | gem 'rspec' | |
12 | gem 'webmock' | |
13 | end | |
14 | | |
15 | gem 'releasinator', '~> 0.6' | |
16 | | |
-------------------------------------------------------------------------------- | |
/LICENSE: | |
-------------------------------------------------------------------------------- | |
1 | The PayPal Ruby SDK is released under the following license: | |
2 | | |
3 | Copyright (c) 2013-2016 PAYPAL, INC. | |
4 | | |
5 | SDK LICENSE | |
6 | | |
7 | NOTICE TO USER: PayPal, Inc. is providing the Software and Documentation for use under the terms of | |
8 | this Agreement. Any use, reproduction, modification or distribution of the Software or Documentation, | |
9 | or any derivatives or portions hereof, constitutes your acceptance of this Agreement. | |
10 | | |
11 | As used in this Agreement, "PayPal" means PayPal, Inc. "Software" means the software code accompanying | |
12 | this agreement. "Documentation" means the documents, specifications and all other items accompanying | |
13 | this Agreement other than the Software. | |
14 | | |
15 | 1. LICENSE GRANT Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, | |
16 | worldwide, royalty free license to use, reproduce, prepare derivative works from, publicly display, | |
17 | publicly perform, distribute and sublicense the Software for any purpose, provided the copyright notice | |
18 | below appears in a conspicuous location within the source code of the distributed Software and this | |
19 | license is distributed in the supporting documentation of the Software you distribute. Furthermore, | |
20 | you must comply with all third party licenses in order to use the third party software contained in the | |
21 | Software. | |
22 | | |
23 | Subject to the terms of this Agreement, PayPal hereby grants you a non-exclusive, worldwide, royalty free | |
24 | license to use, reproduce, publicly display, publicly perform, distribute and sublicense the Documentation | |
25 | for any purpose. You may not modify the Documentation. | |
26 | | |
27 | No title to the intellectual property in the Software or Documentation is transferred to you under the | |
28 | terms of this Agreement. You do not acquire any rights to the Software or the Documentation except as | |
29 | expressly set forth in this Agreement. | |
30 | | |
31 | If you choose to distribute the Software in a commercial product, you do so with the understanding that | |
32 | you agree to defend, indemnify and hold harmless PayPal and its suppliers against any losses, damages and | |
33 | costs arising from the claims, lawsuits or other legal actions arising out of such distribution. You may | |
34 | distribute the Software in object code form under your own license, provided that your license agreement: | |
35 | | |
36 | (a) complies with the terms and conditions of this license agreement; | |
37 | | |
38 | (b) effectively disclaims all warranties and conditions, express or implied, on behalf of PayPal; | |
39 | | |
40 | (c) effectively excludes all liability for damages on behalf of PayPal; | |
41 | | |
42 | (d) states that any provisions that differ from this Agreement are offered by you alone and not PayPal; and | |
43 | | |
44 | (e) states that the Software is available from you or PayPal and informs licensees how to obtain it in a | |
45 | reasonable manner on or through a medium customarily used for software exchange. | |
46 | | |
47 | 2. DISCLAIMER OF WARRANTY | |
48 | PAYPAL LICENSES THE SOFTWARE AND DOCUMENTATION TO YOU ONLY ON AN "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS | |
49 | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY WARRANTIES OR CONDITIONS OF TITLE, | |
50 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. PAYPAL MAKES NO WARRANTY THAT THE | |
51 | SOFTWARE OR DOCUMENTATION WILL BE ERROR-FREE. Each user of the Software or Documentation is solely responsible | |
52 | for determining the appropriateness of using and distributing the Software and Documentation and assumes all | |
53 | risks associated with its exercise of rights under this Agreement, including but not limited to the risks and | |
54 | costs of program errors, compliance with applicable laws, damage to or loss of data, programs, or equipment, | |
55 | and unavailability or interruption of operations. Use of the Software and Documentation is made with the | |
56 | understanding that PayPal will not provide you with any technical or customer support or maintenance. Some | |
57 | states or jurisdictions do not allow the exclusion of implied warranties or limitations on how long an implied | |
58 | warranty may last, so the above limitations may not apply to you. To the extent permissible, any implied | |
59 | warranties are limited to ninety (90) days. | |
60 | | |
61 | | |
62 | 3. LIMITATION OF LIABILITY | |
63 | PAYPAL AND ITS SUPPLIERS SHALL NOT BE LIABLE FOR LOSS OR DAMAGE ARISING OUT OF THIS AGREEMENT OR FROM THE USE | |
64 | OF THE SOFTWARE OR DOCUMENTATION. IN NO EVENT WILL PAYPAL OR ITS SUPPLIERS BE LIABLE TO YOU OR ANY THIRD PARTY | |
65 | FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, INCIDENTAL, OR SPECIAL DAMAGES INCLUDING LOST PROFITS, LOST SAVINGS, | |
66 | COSTS, FEES, OR EXPENSES OF ANY KIND ARISING OUT OF ANY PROVISION OF THIS AGREEMENT OR THE USE OR THE INABILITY | |
67 | TO USE THE SOFTWARE OR DOCUMENTATION, HOWEVER CAUSED AND UNDER ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
68 | STRICT LIABILITY OR TORT INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. | |
69 | PAYPAL'S AGGREGATE LIABILITY AND THAT OF ITS SUPPLIERS UNDER OR IN CONNECTION WITH THIS AGREEMENT SHALL BE | |
70 | LIMITED TO THE AMOUNT PAID BY YOU FOR THE SOFTWARE AND DOCUMENTATION. | |
71 | | |
72 | 4. TRADEMARK USAGE | |
73 | PayPal is a trademark PayPal, Inc. in the United States and other countries. Such trademarks may not be used | |
74 | to endorse or promote any product unless expressly permitted under separate agreement with PayPal. | |
75 | | |
76 | 5. TERM | |
77 | Your rights under this Agreement shall terminate if you fail to comply with any of the material terms or | |
78 | conditions of this Agreement and do not cure such failure in a reasonable period of time after becoming | |
79 | aware of such noncompliance. If all your rights under this Agreement terminate, you agree to cease use | |
80 | and distribution of the Software and Documentation as soon as reasonably practicable. | |
81 | | |
82 | 6. GOVERNING LAW AND JURISDICTION. This Agreement is governed by the statutes and laws of the State of | |
83 | California, without regard to the conflicts of law principles thereof. If any part of this Agreement is | |
84 | found void and unenforceable, it will not affect the validity of the balance of the Agreement, which shall | |
85 | remain valid and enforceable according to its terms. Any dispute arising out of or related to this Agreement | |
86 | shall be brought in the courts of Santa Clara County, California, USA. | |
87 | | |
-------------------------------------------------------------------------------- | |
/Rakefile: | |
-------------------------------------------------------------------------------- | |
1 | require "bundler/gem_tasks" | |
2 | | |
3 | # release will instead be declared by the releasinator | |
4 | Rake::Task["release"].clear | |
5 | | |
6 | spec = Gem::Specification.find_by_name 'releasinator' | |
7 | load "#{spec.gem_dir}/lib/tasks/releasinator.rake" | |
8 | | |
9 | desc "Run tests" | |
10 | task :rspec do | |
11 | cmd = "bundle exec rspec -f d" | |
12 | system(cmd) || raise("#{cmd} failed") | |
13 | end | |
14 | | |
15 | task :default => :rspec | |
16 | | |
-------------------------------------------------------------------------------- | |
/data/DigiCertHighAssuranceEVRootCA.pem: | |
-------------------------------------------------------------------------------- | |
1 | -----BEGIN CERTIFICATE----- | |
2 | MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs | |
3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 | |
4 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j | |
5 | ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL | |
6 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 | |
7 | LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug | |
8 | RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm | |
9 | +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW | |
10 | PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM | |
11 | xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB | |
12 | Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 | |
13 | hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg | |
14 | EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF | |
15 | MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA | |
16 | FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec | |
17 | nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z | |
18 | eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF | |
19 | hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 | |
20 | Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe | |
21 | vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep | |
22 | +OkuE6N36B9K | |
23 | -----END CERTIFICATE----- | |
-------------------------------------------------------------------------------- | |
/data/DigiCertSHA2ExtendedValidationServerCA.pem: | |
-------------------------------------------------------------------------------- | |
1 | -----BEGIN CERTIFICATE----- | |
2 | MIIEtjCCA56gAwIBAgIQDHmpRLCMEZUgkmFf4msdgzANBgkqhkiG9w0BAQsFADBs | |
3 | MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 | |
4 | d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j | |
5 | ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowdTEL | |
6 | MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 | |
7 | LmRpZ2ljZXJ0LmNvbTE0MDIGA1UEAxMrRGlnaUNlcnQgU0hBMiBFeHRlbmRlZCBW | |
8 | YWxpZGF0aW9uIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC | |
9 | ggEBANdTpARR+JmmFkhLZyeqk0nQOe0MsLAAh/FnKIaFjI5j2ryxQDji0/XspQUY | |
10 | uD0+xZkXMuwYjPrxDKZkIYXLBxA0sFKIKx9om9KxjxKws9LniB8f7zh3VFNfgHk/ | |
11 | LhqqqB5LKw2rt2O5Nbd9FLxZS99RStKh4gzikIKHaq7q12TWmFXo/a8aUGxUvBHy | |
12 | /Urynbt/DvTVvo4WiRJV2MBxNO723C3sxIclho3YIeSwTQyJ3DkmF93215SF2AQh | |
13 | cJ1vb/9cuhnhRctWVyh+HA1BV6q3uCe7seT6Ku8hI3UarS2bhjWMnHe1c63YlC3k | |
14 | 8wyd7sFOYn4XwHGeLN7x+RAoGTMCAwEAAaOCAUkwggFFMBIGA1UdEwEB/wQIMAYB | |
15 | Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEF | |
16 | BQcDAjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp | |
17 | Z2ljZXJ0LmNvbTBLBgNVHR8ERDBCMECgPqA8hjpodHRwOi8vY3JsNC5kaWdpY2Vy | |
18 | dC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3JsMD0GA1UdIAQ2 | |
19 | MDQwMgYEVR0gADAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5j | |
20 | b20vQ1BTMB0GA1UdDgQWBBQ901Cl1qCt7vNKYApl0yHU+PjWDzAfBgNVHSMEGDAW | |
21 | gBSxPsNpA/i/RwHUmCYaCALvY2QrwzANBgkqhkiG9w0BAQsFAAOCAQEAnbbQkIbh | |
22 | hgLtxaDwNBx0wY12zIYKqPBKikLWP8ipTa18CK3mtlC4ohpNiAexKSHc59rGPCHg | |
23 | 4xFJcKx6HQGkyhE6V6t9VypAdP3THYUYUN9XR3WhfVUgLkc3UHKMf4Ib0mKPLQNa | |
24 | 2sPIoc4sUqIAY+tzunHISScjl2SFnjgOrWNoPLpSgVh5oywM395t6zHyuqB8bPEs | |
25 | 1OG9d4Q3A84ytciagRpKkk47RpqF/oOi+Z6Mo8wNXrM9zwR4jxQUezKcxwCmXMS1 | |
26 | oVWNWlZopCJwqjyBcdmdqEU79OX2olHdx3ti6G8MdOu42vi/hw15UJGQmxg7kVkn | |
27 | 8TUoE6smftX3eg== | |
28 | -----END CERTIFICATE----- | |
-------------------------------------------------------------------------------- | |
/lib/generators/paypal/sdk/USAGE: | |
-------------------------------------------------------------------------------- | |
1 | To copy a PayPal SDK default configuration and initializer to your Rails App. | |
2 | | |
3 | rails g paypal-sdk:install | |
4 | | |
-------------------------------------------------------------------------------- | |
/lib/generators/paypal/sdk/install_generator.rb: | |
-------------------------------------------------------------------------------- | |
1 | module Paypal | |
2 | module Sdk | |
3 | module Generators | |
4 | class InstallGenerator < Rails::Generators::Base | |
5 | source_root File.expand_path('../templates', __FILE__) | |
6 | | |
7 | def copy_config_file | |
8 | copy_file "paypal.yml", "config/paypal.yml" | |
9 | end | |
10 | | |
11 | def copy_initializer_file | |
12 | copy_file "paypal.rb", "config/initializers/paypal.rb" | |
13 | end | |
14 | end | |
15 | end | |
16 | end | |
17 | end | |
18 | | |
-------------------------------------------------------------------------------- | |
/lib/generators/paypal/sdk/templates/paypal.rb: | |
-------------------------------------------------------------------------------- | |
1 | PayPal::SDK.load("config/paypal.yml", Rails.env) | |
2 | PayPal::SDK.logger = Rails.logger | |
3 | | |
-------------------------------------------------------------------------------- | |
/lib/generators/paypal/sdk/templates/paypal.yml: | |
-------------------------------------------------------------------------------- | |
1 | test: &default | |
2 | | |
3 | # Credentials for REST APIs | |
4 | client_id: EBWKjlELKMYqRNQ6sYvFo64FtaRLRR5BdHEESmha49TM | |
5 | client_secret: EO422dn3gQLgDbuwqTjzrFgFtaRLRR5BdHEESmha49TM | |
6 | | |
7 | # Mode can be 'live' or 'sandbox' | |
8 | mode: sandbox | |
9 | | |
10 | # Credentials for Classic APIs | |
11 | app_id: APP-80W284485P519543T | |
12 | username: jb-us-seller_api1.paypal.com | |
13 | password: WX4WTU3S8MY44S7F | |
14 | signature: AFcWxV21C7fd0v3bYYYRCpSSRl31A7yDhhsPUU2XhtMoZXsWHFxu-RWy | |
15 | # # With Certificate | |
16 | # cert_path: "config/cert_key.pem" | |
17 | sandbox_email_address: [email protected] | |
18 | | |
19 | # # IP Address | |
20 | # ip_address: 127.0.0.1 | |
21 | # # HTTP Proxy | |
22 | # http_proxy: http://proxy-ipaddress:3129/ | |
23 | | |
24 | development: | |
25 | <<: *default | |
26 | | |
27 | production: | |
28 | <<: *default | |
29 | mode: live | |
30 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk-core.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module Core | |
4 | | |
5 | autoload :VERSION, "paypal-sdk/rest/version" | |
6 | autoload :Config, "paypal-sdk/core/config" | |
7 | autoload :Configuration, "paypal-sdk/core/config" | |
8 | autoload :Logging, "paypal-sdk/core/logging" | |
9 | autoload :Authentication, "paypal-sdk/core/authentication" | |
10 | autoload :Exceptions, "paypal-sdk/core/exceptions" | |
11 | autoload :OpenIDConnect, "paypal-sdk/core/openid_connect" | |
12 | autoload :API, "paypal-sdk/core/api" | |
13 | autoload :Util, "paypal-sdk/core/util" | |
14 | autoload :Credential, "paypal-sdk/core/credential" | |
15 | | |
16 | end | |
17 | | |
18 | autoload :OpenIDConnect, "paypal-sdk/core/openid_connect" | |
19 | | |
20 | class << self | |
21 | def configure(options = {}, &block) | |
22 | Core::Config.configure(options, &block) | |
23 | end | |
24 | | |
25 | def load(*args) | |
26 | Core::Config.load(*args) | |
27 | end | |
28 | | |
29 | def logger | |
30 | Core::Config.logger | |
31 | end | |
32 | | |
33 | def logger=(log) | |
34 | Core::Config.logger = log | |
35 | end | |
36 | end | |
37 | end | |
38 | end | |
39 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk-rest.rb: | |
-------------------------------------------------------------------------------- | |
1 | require "paypal-sdk/rest" | |
2 | | |
3 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module Core | |
4 | module API | |
5 | autoload :Base, "paypal-sdk/core/api/base" | |
6 | autoload :Merchant, "paypal-sdk/core/api/merchant" | |
7 | autoload :Platform, "paypal-sdk/core/api/platform" | |
8 | autoload :REST, "paypal-sdk/core/api/rest" | |
9 | autoload :IPN, "paypal-sdk/core/api/ipn" | |
10 | | |
11 | module DataTypes | |
12 | autoload :Base, "paypal-sdk/core/api/data_types/base" | |
13 | autoload :Enum, "paypal-sdk/core/api/data_types/enum" | |
14 | autoload :SimpleTypes, "paypal-sdk/core/api/data_types/simple_types" | |
15 | autoload :ArrayWithBlock, "paypal-sdk/core/api/data_types/array_with_block" | |
16 | end | |
17 | end | |
18 | end | |
19 | end | |
20 | end | |
21 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/base.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk/rest/version' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | | |
5 | module API | |
6 | # API class provide default functionality for accessing the API web services. | |
7 | # == Example | |
8 | # api = API::Base.new("AdaptivePayments") | |
9 | # response = api.request("GetPaymentOptions", "") | |
10 | class Base | |
11 | | |
12 | include Util::HTTPHelper | |
13 | | |
14 | attr_accessor :http, :uri, :service_name | |
15 | | |
16 | DEFAULT_API_MODE = :sandbox | |
17 | API_MODES = [ :live, :sandbox ] | |
18 | | |
19 | # Initialize API object | |
20 | # === Argument | |
21 | # * <tt>service_name</tt> -- (Optional) Service name | |
22 | # * <tt>environment</tt> -- (Optional) Configuration environment to load | |
23 | # * <tt>options</tt> -- (Optional) Override configuration. | |
24 | # === Example | |
25 | # new("AdaptivePayments") | |
26 | # new("AdaptivePayments", :development) | |
27 | # new(:wsdl_service) # It load wsdl_service configuration | |
28 | def initialize(service_name = "", environment = nil, options = {}) | |
29 | unless service_name.is_a? String | |
30 | environment, options, service_name = service_name, environment || {}, "" | |
31 | end | |
32 | @service_name = service_name | |
33 | set_config(environment, options) | |
34 | end | |
35 | | |
36 | def uri | |
37 | @uri ||= | |
38 | begin | |
39 | uri = URI.parse("#{service_endpoint}/#{service_name}") | |
40 | uri.path = uri.path.gsub(/\/+/, "/") | |
41 | uri | |
42 | end | |
43 | end | |
44 | | |
45 | def http | |
46 | @http ||= create_http_connection(uri) | |
47 | end | |
48 | | |
49 | # Override set_config method to create http connection on changing the configuration. | |
50 | def set_config(*args) | |
51 | @http = @uri = nil | |
52 | super | |
53 | end | |
54 | | |
55 | # Get configured API mode( sandbox or live) | |
56 | def api_mode | |
57 | if config.mode and API_MODES.include? config.mode.to_sym | |
58 | config.mode.to_sym | |
59 | else | |
60 | DEFAULT_API_MODE | |
61 | end | |
62 | end | |
63 | | |
64 | # Get service end point | |
65 | def service_endpoint | |
66 | config.endpoint | |
67 | end | |
68 | | |
69 | # Default Http header | |
70 | def default_http_header | |
71 | { "User-Agent" => self.class.user_agent } | |
72 | end | |
73 | | |
74 | # Generate HTTP request for given action and parameters | |
75 | # === Arguments | |
76 | # * <tt>http_method</tt> -- HTTP method(get/put/post/delete/patch) | |
77 | # * <tt>action</tt> -- Action to perform | |
78 | # * <tt>params</tt> -- (Optional) Parameters for the action | |
79 | # * <tt>initheader</tt> -- (Optional) HTTP header | |
80 | def api_call(payload) | |
81 | payload[:header] = default_http_header.merge(payload[:header]) | |
82 | payload[:uri] ||= uri.dup | |
83 | payload[:http] ||= http.dup | |
84 | payload[:uri].query = encode_www_form(payload[:query]) if payload[:query] and payload[:query].any? | |
85 | format_request(payload) | |
86 | payload[:response] = http_call(payload) | |
87 | format_response(payload) | |
88 | payload[:data] | |
89 | end | |
90 | | |
91 | # Generate HTTP request for given action and parameters | |
92 | # === Arguments | |
93 | # * <tt>action</tt> -- Action to perform | |
94 | # * <tt>params</tt> -- (Optional) Parameters for the action | |
95 | # * <tt>initheader</tt> -- (Optional) HTTP header | |
96 | def post(action, params = {}, header = {}, query = {}) | |
97 | action, params, header = "", action, params if action.is_a? Hash | |
98 | api_call(:method => :post, :action => action, :query => query, :params => params, :header => header) | |
99 | end | |
100 | alias_method :request, :post | |
101 | | |
102 | def get(action, params = {}, header = {}) | |
103 | action, params, header = "", action, params if action.is_a? Hash | |
104 | api_call(:method => :get, :action => action, :query => params, :params => nil, :header => header) | |
105 | end | |
106 | | |
107 | def patch(action, params = {}, header = {}) | |
108 | action, params, header = "", action, params if action.is_a? Hash | |
109 | api_call(:method => :patch, :action => action, :params => params, :header => header) | |
110 | end | |
111 | | |
112 | def put(action, params = {}, header = {}) | |
113 | action, params, header = "", action, params if action.is_a? Hash | |
114 | api_call(:method => :put, :action => action, :params => params, :header => header) | |
115 | end | |
116 | | |
117 | def delete(action, params = {}, header = {}) | |
118 | action, params, header = "", action, params if action.is_a? Hash | |
119 | api_call(:method => :delete, :action => action, :params => params, :header => header) | |
120 | end | |
121 | | |
122 | # Format Request data. It will be override by child class | |
123 | # == Arguments | |
124 | # * <tt>action</tt> -- Request action | |
125 | # * <tt>params</tt> -- Request parameters | |
126 | # == Return | |
127 | # * <tt>path</tt> -- Formated request uri object | |
128 | # * <tt>params</tt> -- Formated request Parameters | |
129 | # * <tt>header</tt> -- HTTP Header | |
130 | def format_request(payload) | |
131 | payload[:uri].path = url_join(payload[:uri].path, payload[:action]) | |
132 | payload[:body] = payload[:params].to_s | |
133 | payload | |
134 | end | |
135 | | |
136 | # Format Response object. It will be override by child class | |
137 | # == Argument | |
138 | # * <tt>action</tt> -- Request action | |
139 | # * <tt>response</tt> -- HTTP response object | |
140 | def format_response(payload) | |
141 | payload[:data] = payload[:response].body | |
142 | payload | |
143 | end | |
144 | | |
145 | # Format Error object. It will be override by child class. | |
146 | # == Arguments | |
147 | # * <tt>exception</tt> -- Exception object. | |
148 | # * <tt>message</tt> -- Readable error message. | |
149 | def format_error(exception, message) | |
150 | raise exception | |
151 | end | |
152 | | |
153 | class << self | |
154 | def sdk_library_details | |
155 | @library_details ||= "paypal-sdk-core #{PayPal::SDK::REST::VERSION}; ruby #{RUBY_VERSION}p#{RUBY_PATCHLEVEL}-#{RUBY_PLATFORM}" | |
156 | begin | |
157 | @library_details << ";#{OpenSSL::OPENSSL_LIBRARY_VERSION}" | |
158 | rescue NameError | |
159 | @library_details << ";OpenSSL #{OpenSSL::OPENSSL_VERSION}" | |
160 | end | |
161 | end | |
162 | | |
163 | def user_agent | |
164 | @user_agent ||= "PayPalSDK/rest-sdk-ruby #{PayPal::SDK::REST::VERSION} (#{sdk_library_details})" | |
165 | end | |
166 | end | |
167 | end | |
168 | end | |
169 | end | |
170 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/data_types/array_with_block.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module API | |
3 | module DataTypes | |
4 | # Create Array with block. | |
5 | # === Example | |
6 | # ary = ArrayWithBlock.new{|val| val.to_i } | |
7 | # ary.push("123") # [ 123 ] | |
8 | # ary.merge!(["1", "3"]) # [ 123, 1, 3 ] | |
9 | class ArrayWithBlock < ::Array | |
10 | def initialize(&block) | |
11 | @block = block | |
12 | super() | |
13 | end | |
14 | | |
15 | def [](key) | |
16 | super(key) || send(:"[]=", key, nil) | |
17 | end | |
18 | | |
19 | def []=(key, value) | |
20 | super(key, @block ? @block.call(value) : value) | |
21 | end | |
22 | | |
23 | def push(value) | |
24 | super(@block ? @block.call(value) : value) | |
25 | end | |
26 | | |
27 | def merge!(array) | |
28 | if array.is_a? Array | |
29 | array.each do |object| | |
30 | push(object) | |
31 | end | |
32 | elsif array.is_a? Hash and array.keys.first.to_s =~ /^\d+$/ | |
33 | array.each do |key, object| | |
34 | self[key.to_i] = object | |
35 | end | |
36 | else | |
37 | push(array) | |
38 | end | |
39 | self | |
40 | end | |
41 | end | |
42 | end | |
43 | end | |
44 | end | |
45 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/data_types/base.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'date' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | module API | |
5 | | |
6 | module DataTypes | |
7 | | |
8 | # Create attributes and restrict the object type. | |
9 | # == Example | |
10 | # class ConvertCurrencyRequest < Core::API::DataTypes::Base | |
11 | # object_of :baseAmountList, CurrencyList | |
12 | # object_of :convertToCurrencyList, CurrencyCodeList | |
13 | # object_of :countryCode, String | |
14 | # object_of :conversionType, String | |
15 | # end | |
16 | class Base | |
17 | | |
18 | HashOptions = { :attribute => true, :namespace => true, :symbol => false } | |
19 | ContentKey = :value | |
20 | | |
21 | include SimpleTypes | |
22 | include Logging | |
23 | | |
24 | class << self | |
25 | | |
26 | # Add attribute | |
27 | # === Arguments | |
28 | # * <tt>name</tt> -- attribute name | |
29 | # * <tt>options</tt> -- options | |
30 | def add_attribute(name, options = {}) | |
31 | add_member(name, SimpleTypes::String, options.merge( :attribute => true )) | |
32 | end | |
33 | | |
34 | # Fields list for the DataTye | |
35 | def members | |
36 | @members ||= | |
37 | begin | |
38 | superclass.load_members if defined? superclass.load_members | |
39 | parent_members = superclass.instance_variable_get("@members") | |
40 | parent_members ? parent_members.dup : Util::OrderedHash.new | |
41 | end | |
42 | end | |
43 | | |
44 | # Add Field to class variable hash and generate methods | |
45 | # === Example | |
46 | # add_member(:errorMessage, String) # Generate Code | |
47 | # # attr_reader :errorMessage | |
48 | # # alias_method :error_message, :errorMessage | |
49 | # # alias_method :error_message=, :errorMessage= | |
50 | def add_member(member_name, klass, options = {}) | |
51 | member_name = member_name.to_sym | |
52 | return if members[member_name] | |
53 | members[member_name] = options.merge( :type => klass ) | |
54 | member_variable_name = "@#{member_name}" | |
55 | define_method "#{member_name}=" do |value| | |
56 | object = options[:array] ? convert_array(value, klass) : convert_object(value, klass) | |
57 | instance_variable_set(member_variable_name, object) | |
58 | end | |
59 | default_value = ( options[:array] ? [] : ( klass < Base ? Util::OrderedHash.new : nil ) ) | |
60 | define_method member_name do |&block| | |
61 | value = instance_variable_get(member_variable_name) || ( default_value && send("#{member_name}=", default_value) ) | |
62 | value.instance_eval(&block) if block | |
63 | value | |
64 | end | |
65 | define_alias_methods(member_name, options) | |
66 | end | |
67 | | |
68 | # Define alias methods for getter and setter | |
69 | def define_alias_methods(member_name, options) | |
70 | snakecase_name = snakecase(member_name) | |
71 | alias_method snakecase_name, member_name | |
72 | alias_method "#{snakecase_name}=", "#{member_name}=" | |
73 | alias_method "#{options[:namespace]}:#{member_name}=", "#{member_name}=" if options[:namespace] | |
74 | if options[:attribute] | |
75 | alias_method "@#{member_name}=", "#{member_name}=" | |
76 | alias_method "@#{options[:namespace]}:#{member_name}=", "#{member_name}=" if options[:namespace] | |
77 | end | |
78 | end | |
79 | | |
80 | # define method for given member and the class name | |
81 | # === Example | |
82 | # object_of(:errorMessage, ErrorMessage) # Generate Code | |
83 | # # def errorMessage=(options) | |
84 | # # @errorMessage = ErrorMessage.new(options) | |
85 | # # end | |
86 | # # add_member :errorMessage, ErrorMessage | |
87 | def object_of(key, klass, options = {}) | |
88 | add_member(key, klass, options) | |
89 | end | |
90 | | |
91 | # define method for given member and the class name | |
92 | # === Example | |
93 | # array_of(:errorMessage, ErrorMessage) # It Generate below code | |
94 | # # def errorMessage=(array) | |
95 | # # @errorMessage = array.map{|options| ErrorMessage.new(options) } | |
96 | # # end | |
97 | # # add_member :errorMessage, ErrorMessage | |
98 | def array_of(key, klass, options = {}) | |
99 | add_member(key, klass, options.merge(:array => true)) | |
100 | end | |
101 | | |
102 | # Generate snakecase string. | |
103 | # === Example | |
104 | # snakecase("errorMessage") | |
105 | # # error_message | |
106 | def snakecase(string) | |
107 | string.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').gsub(/([A-Z])([A-Z][a-z])/, '\1_\2').downcase | |
108 | end | |
109 | | |
110 | end | |
111 | | |
112 | # Initialize options. | |
113 | def initialize(options = {}, &block) | |
114 | merge!(options, &block) | |
115 | end | |
116 | | |
117 | # Merge values | |
118 | def merge!(options, &block) | |
119 | if options.is_a? Hash | |
120 | options.each do |key, value| | |
121 | set(key, value) | |
122 | end | |
123 | elsif members[ContentKey] | |
124 | set(ContentKey, options) | |
125 | else | |
126 | raise ArgumentError, "invalid data(#{options.inspect}) for #{self.class.name} class" | |
127 | end | |
128 | self.instance_eval(&block) if block | |
129 | self | |
130 | end | |
131 | | |
132 | # Set value for given member | |
133 | # === Arguments | |
134 | # * <tt>key</tt> -- member name | |
135 | # * <tt>value</tt> -- value for member | |
136 | def set(key, value) | |
137 | send("#{key}=", value) | |
138 | rescue NoMethodError => error | |
139 | # Uncomment to see missing fields | |
140 | # logger.debug error.message | |
141 | rescue TypeError, ArgumentError => error | |
142 | raise TypeError, "#{error.message}(#{value.inspect}) for #{self.class.name}.#{key} member" | |
143 | end | |
144 | | |
145 | # Create array of objects. | |
146 | # === Example | |
147 | # covert_array([{ :amount => "55", :code => "USD"}], CurrencyType) | |
148 | # covert_array({ "0" => { :amount => "55", :code => "USD"} }, CurrencyType) | |
149 | # covert_array({ :amount => "55", :code => "USD"}, CurrencyType) | |
150 | # # @return | |
151 | # # [ <CurrencyType#object @amount="55" @code="USD" > ] | |
152 | def convert_array(array, klass) | |
153 | default_value = ( klass < Base ? Util::OrderedHash.new : nil ) | |
154 | data_type_array = ArrayWithBlock.new{|object| convert_object(object || default_value, klass) } | |
155 | data_type_array.merge!(array) | |
156 | end | |
157 | | |
158 | # Create object based on given data. | |
159 | # === Example | |
160 | # covert_object({ :amount => "55", :code => "USD"}, CurrencyType ) | |
161 | # # @return | |
162 | # # <CurrencyType#object @amount="55" @code="USD" > | |
163 | def convert_object(object, klass) | |
164 | object.is_a?(klass) ? object : ( ( object.nil? or object == "" ) ? nil : klass.new(object) ) | |
165 | end | |
166 | | |
167 | # Alias instance method for the class method. | |
168 | def members | |
169 | self.class.members | |
170 | end | |
171 | | |
172 | # Get configured member names | |
173 | def member_names | |
174 | members.keys | |
175 | end | |
176 | | |
177 | # Create Hash based configured members | |
178 | def to_hash(options = {}) | |
179 | options = HashOptions.merge(options) | |
180 | hash = Util::OrderedHash.new | |
181 | member_names.each do |member| | |
182 | value = value_to_hash(instance_variable_get("@#{member}"), options) | |
183 | hash[hash_key(member, options)] = value unless skip_value?(value) | |
184 | end | |
185 | hash | |
186 | end | |
187 | | |
188 | # Skip nil, empty array and empty hash. | |
189 | def skip_value?(value) | |
190 | value.nil? || ( ( value.is_a?(Array) || value.is_a?(Hash) ) && value.empty? ) | |
191 | end | |
192 | | |
193 | # Generate Hash key for given member name based on configuration | |
194 | # === Example | |
195 | # hash_key(:amount) # @return :"ebl:amount" | |
196 | # hash_key(:type) # @return :"@type" | |
197 | def hash_key(key, options = {}) | |
198 | unless key == ContentKey | |
199 | member_option = members[key] | |
200 | key = member_option[:name] if member_option.include? :name | |
201 | if !member_option[:attribute] and member_option[:namespace] and options[:namespace] | |
202 | key = "#{member_option[:namespace]}:#{key}" | |
203 | end | |
204 | key = "@#{key}" if member_option[:attribute] and options[:attribute] | |
205 | end | |
206 | options[:symbol] ? key.to_sym : key.to_s | |
207 | end | |
208 | | |
209 | # Covert the object to hash based on class. | |
210 | def value_to_hash(value, options = {}) | |
211 | case value | |
212 | when Array | |
213 | value = value.map{|object| value_to_hash(object, options) } | |
214 | value.delete_if{|v| skip_value?(v) } | |
215 | value | |
216 | when Base | |
217 | value.to_hash(options) | |
218 | else | |
219 | value | |
220 | end | |
221 | end | |
222 | end | |
223 | end | |
224 | end | |
225 | end | |
226 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/data_types/enum.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module API | |
3 | module DataTypes | |
4 | | |
5 | class Enum < SimpleTypes::String | |
6 | class << self | |
7 | def options | |
8 | @options ||= [] | |
9 | end | |
10 | | |
11 | def options=(options) | |
12 | if options.is_a? Hash | |
13 | options.each do |const_name, value| | |
14 | const_set(const_name, value) | |
15 | end | |
16 | @options = options.values | |
17 | else | |
18 | @options = options | |
19 | end | |
20 | end | |
21 | end | |
22 | end | |
23 | | |
24 | end | |
25 | end | |
26 | end | |
27 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/data_types/simple_types.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'date' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | module API | |
5 | module DataTypes | |
6 | | |
7 | module SimpleTypes | |
8 | class String < ::String | |
9 | def self.new(string = "") | |
10 | string.is_a?(::String) ? super : super(string.to_s) | |
11 | end | |
12 | | |
13 | def to_yaml_type | |
14 | "!tag:yaml.org,2002:str" | |
15 | end | |
16 | end | |
17 | | |
18 | class Integer < ::Integer | |
19 | def self.new(number) | |
20 | number.to_i | |
21 | end | |
22 | end | |
23 | | |
24 | class Float < ::Float | |
25 | def self.new(float) | |
26 | float.to_f | |
27 | end | |
28 | end | |
29 | | |
30 | class Boolean | |
31 | def self.new(boolean) | |
32 | ( boolean == 0 || boolean == "" || boolean =~ /^(false|f|no|n|0)$/i ) ? false : !!boolean | |
33 | end | |
34 | end | |
35 | | |
36 | class Date < ::Date | |
37 | def self.new(date) | |
38 | date.is_a?(::Date) ? date : Date.parse(date.to_s) | |
39 | end | |
40 | end | |
41 | | |
42 | class DateTime < ::DateTime | |
43 | def self.new(date_time) | |
44 | date_time = "0001-01-01T00:00:00" if date_time.to_s == "0" | |
45 | date_time.is_a?(::DateTime) ? date_time : parse(date_time.to_s) | |
46 | end | |
47 | end | |
48 | end | |
49 | | |
50 | end | |
51 | end | |
52 | end | |
53 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/ipn.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module Core | |
4 | module API | |
5 | module IPN | |
6 | | |
7 | END_POINTS = { | |
8 | :sandbox => "https://www.sandbox.paypal.com/cgi-bin/webscr", | |
9 | :live => "https://ipnpb.paypal.com/cgi-bin/webscr" | |
10 | } | |
11 | VERIFIED = "VERIFIED" | |
12 | INVALID = "INVALID" | |
13 | | |
14 | class Message | |
15 | include Util::HTTPHelper | |
16 | | |
17 | attr_accessor :message | |
18 | | |
19 | def initialize(message, env = nil, options = {}) | |
20 | @message = message | |
21 | set_config(env, options) | |
22 | end | |
23 | | |
24 | # Fetch end point | |
25 | def ipn_endpoint | |
26 | config.ipn_endpoint || default_ipn_endpoint | |
27 | end | |
28 | | |
29 | # Default IPN end point | |
30 | def default_ipn_endpoint | |
31 | endpoint = END_POINTS[(config.mode || :sandbox).to_sym] rescue nil | |
32 | endpoint || END_POINTS[:sandbox] | |
33 | end | |
34 | | |
35 | # Request IPN service for validating the content | |
36 | # === Return | |
37 | # return http response object | |
38 | def request | |
39 | uri = URI(ipn_endpoint) | |
40 | query_string = "cmd=_notify-validate&#{message}" | |
41 | http_call(:method => :post, :uri => uri, :body => query_string) | |
42 | end | |
43 | | |
44 | # Validate the given content | |
45 | # === Return | |
46 | # return true or false | |
47 | def valid? | |
48 | request.body == VERIFIED | |
49 | end | |
50 | end | |
51 | | |
52 | class << self | |
53 | def valid?(*args) | |
54 | Message.new(*args).valid? | |
55 | end | |
56 | | |
57 | def request(*args) | |
58 | Message.new(*args).request | |
59 | end | |
60 | end | |
61 | | |
62 | end | |
63 | end | |
64 | end | |
65 | end | |
66 | end | |
67 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/api/rest.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'multi_json' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | module API | |
5 | class REST < Base | |
6 | NVP_AUTH_HEADER = { | |
7 | :sandbox_email_address => "X-PAYPAL-SANDBOX-EMAIL-ADDRESS", | |
8 | :device_ipaddress => "X-PAYPAL-DEVICE-IPADDRESS" | |
9 | } | |
10 | DEFAULT_HTTP_HEADER = { | |
11 | "Content-Type" => "application/json" | |
12 | } | |
13 | | |
14 | DEFAULT_REST_END_POINTS = { | |
15 | :sandbox => "https://api.sandbox.paypal.com", | |
16 | :live => "https://api.paypal.com", | |
17 | } | |
18 | TOKEN_REQUEST_PARAMS = "grant_type=client_credentials" | |
19 | | |
20 | # Get REST service end point | |
21 | def service_endpoint | |
22 | config.rest_endpoint || super || DEFAULT_REST_END_POINTS[api_mode] | |
23 | end | |
24 | | |
25 | # Token endpoint | |
26 | def token_endpoint | |
27 | config.rest_token_endpoint || service_endpoint | |
28 | end | |
29 | | |
30 | # Clear cached values. | |
31 | def set_config(*args) | |
32 | @token_uri = nil | |
33 | @token_hash = nil | |
34 | super | |
35 | end | |
36 | | |
37 | # URI object token endpoint | |
38 | def token_uri | |
39 | @token_uri ||= | |
40 | begin | |
41 | new_uri = URI.parse(token_endpoint) | |
42 | new_uri.path = "/v1/oauth2/token" if new_uri.path =~ /^\/?$/ | |
43 | new_uri | |
44 | end | |
45 | end | |
46 | | |
47 | # Generate Oauth token or Get cached | |
48 | def token_hash(auth_code=nil, headers=nil) | |
49 | validate_token_hash | |
50 | @token_hash ||= | |
51 | begin | |
52 | @token_request_at = Time.now | |
53 | basic_auth = ["#{config.client_id}:#{config.client_secret}"].pack('m').delete("\r\n") | |
54 | token_headers = default_http_header.merge({ | |
55 | "Content-Type" => "application/x-www-form-urlencoded", | |
56 | "Authorization" => "Basic #{basic_auth}" }).merge(headers) | |
57 | if auth_code != nil | |
58 | TOKEN_REQUEST_PARAMS.replace "grant_type=authorization_code&response_type=token&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&code=" | |
59 | TOKEN_REQUEST_PARAMS << auth_code | |
60 | end | |
61 | response = http_call( :method => :post, :uri => token_uri, :body => TOKEN_REQUEST_PARAMS, :header => token_headers ) | |
62 | MultiJson.load(response.body, :symbolize_keys => true) | |
63 | end | |
64 | end | |
65 | attr_writer :token_hash | |
66 | | |
67 | # Get access token | |
68 | def token(auth_code=nil, headers={}) | |
69 | token_hash(auth_code, headers)[:access_token] | |
70 | end | |
71 | | |
72 | # Get access token type | |
73 | def token_type(headers={}) | |
74 | token_hash(nil, headers)[:token_type] || "Bearer" | |
75 | end | |
76 | | |
77 | # token setter | |
78 | def token=(new_token) | |
79 | @token_hash = { :access_token => new_token, :token_type => "Bearer" } | |
80 | end | |
81 | | |
82 | # Check token expired or not | |
83 | def validate_token_hash | |
84 | if @token_request_at and | |
85 | @token_hash and @token_hash[:expires_in] and | |
86 | (Time.now - @token_request_at) > @token_hash[:expires_in].to_i | |
87 | @token_hash = nil | |
88 | end | |
89 | end | |
90 | | |
91 | # Override the API call to handle Token Expire | |
92 | def api_call(payload) | |
93 | backup_payload = payload.dup | |
94 | begin | |
95 | response = super(payload) | |
96 | rescue UnauthorizedAccess => error | |
97 | if @token_hash and config.client_id | |
98 | # Reset cached token and Retry api request | |
99 | @token_hash = nil | |
100 | response = super(backup_payload) | |
101 | else | |
102 | raise error | |
103 | end | |
104 | end | |
105 | response | |
106 | end | |
107 | | |
108 | # Validate HTTP response | |
109 | def handle_response(response) | |
110 | super | |
111 | rescue BadRequest => error | |
112 | # Catch BadRequest to get validation error message from the response. | |
113 | error.response | |
114 | end | |
115 | | |
116 | # Format request payload | |
117 | # === Argument | |
118 | # * payload( uri, action, params, header) | |
119 | # === Generate | |
120 | # * payload( uri, body, header ) | |
121 | def format_request(payload) | |
122 | # Request URI | |
123 | payload[:uri].path = url_join(payload[:uri].path, payload[:action]) | |
124 | # HTTP Header | |
125 | credential_properties = credential(payload[:uri].to_s).properties | |
126 | header = map_header_value(NVP_AUTH_HEADER, credential_properties) | |
127 | payload[:header] = header.merge("Authorization" => "#{token_type(payload[:header])} #{token(nil, payload[:header])}"). | |
128 | merge(DEFAULT_HTTP_HEADER).merge(payload[:header]) | |
129 | # Post Data | |
130 | payload[:body] = MultiJson.dump(payload[:params]) | |
131 | payload | |
132 | end | |
133 | | |
134 | # Format response payload | |
135 | # === Argument | |
136 | # * payload( response ) | |
137 | # === Generate | |
138 | # * payload( data ) | |
139 | def format_response(payload) | |
140 | response = payload[:response] | |
141 | payload[:data] = | |
142 | if response.body && response.body.strip == "" | |
143 | {} | |
144 | elsif response.code >= "200" and response.code <= "299" | |
145 | response.body && response.content_type == "application/json" ? MultiJson.load(response.body) : {} | |
146 | elsif response.content_type == "application/json" | |
147 | { "error" => flat_hash(MultiJson.load(response.body)) } | |
148 | else | |
149 | { "error" => { "name" => response.code, "message" => response.message, | |
150 | "developer_msg" => response } } | |
151 | end | |
152 | payload | |
153 | end | |
154 | | |
155 | def flat_hash(h) | |
156 | new_hash = {} | |
157 | h.each_pair do |key, val| | |
158 | if val.is_a?(Hash) | |
159 | new_hash.merge!(flat_hash(val)) | |
160 | else | |
161 | new_hash[key] = val | |
162 | end | |
163 | end | |
164 | new_hash | |
165 | end | |
166 | | |
167 | # Log PayPal-Request-Id header | |
168 | def log_http_call(payload) | |
169 | if payload[:header] and payload[:header]["PayPal-Request-Id"] | |
170 | logger.info "PayPal-Request-Id: #{payload[:header]["PayPal-Request-Id"]}" | |
171 | end | |
172 | super | |
173 | end | |
174 | | |
175 | end | |
176 | end | |
177 | end | |
178 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/authentication.rb: | |
-------------------------------------------------------------------------------- | |
1 | | |
2 | module PayPal::SDK::Core | |
3 | | |
4 | # Contains methods to format credentials for HTTP protocol. | |
5 | # == Example | |
6 | # include Authentication | |
7 | # credential(url) | |
8 | # base_credential | |
9 | # third_party_credential(url) | |
10 | # | |
11 | # add_certificate(http) | |
12 | module Authentication | |
13 | | |
14 | include Configuration | |
15 | | |
16 | # Get credential object | |
17 | # === Argument | |
18 | # * <tt>url</tt> -- API request url | |
19 | def credential(url) | |
20 | third_party_credential(url) || base_credential | |
21 | end | |
22 | | |
23 | # Get base credential | |
24 | def base_credential | |
25 | @base_credential ||= | |
26 | if config.cert_path | |
27 | Credential::Certificate.new(config) | |
28 | else | |
29 | Credential::Signature.new(config) | |
30 | end | |
31 | end | |
32 | | |
33 | # Get base credential type | |
34 | def base_credential_type | |
35 | config.cert_path ? :certificate : :three_token | |
36 | end | |
37 | | |
38 | # Get third party credential | |
39 | def third_party_credential(url) | |
40 | if config.token and config.token_secret | |
41 | Credential::ThirdParty::Token.new(base_credential, config, url) | |
42 | elsif config.subject | |
43 | Credential::ThirdParty::Subject.new(base_credential, config) | |
44 | end | |
45 | end | |
46 | | |
47 | # Clear cached variables on changing the configuration. | |
48 | def set_config(*args) | |
49 | @base_credential = nil | |
50 | super | |
51 | end | |
52 | | |
53 | # Configure ssl certificate to HTTP object | |
54 | # === Argument | |
55 | # * <tt>http</tt> -- Net::HTTP object | |
56 | def add_certificate(http) | |
57 | if base_credential.is_a? Credential::Certificate | |
58 | http.cert = base_credential.cert | |
59 | http.key = base_credential.key | |
60 | else | |
61 | http.cert = nil | |
62 | http.key = nil | |
63 | end | |
64 | end | |
65 | end | |
66 | end | |
67 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/config.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'erb' | |
2 | require 'yaml' | |
3 | | |
4 | module PayPal::SDK::Core | |
5 | | |
6 | # Include Configuration module to access configuration from any object | |
7 | # == Examples | |
8 | # # Include in any class | |
9 | # include Configuration | |
10 | # | |
11 | # # Access config object and attributes | |
12 | # config | |
13 | # config.username | |
14 | # | |
15 | # # Change configuration | |
16 | # set_config(:development) | |
17 | module Configuration | |
18 | | |
19 | # To get default Config object. | |
20 | def config | |
21 | @config ||= Config.config | |
22 | end | |
23 | | |
24 | # To change the configuration to given environment or configuration | |
25 | # === Arguments | |
26 | # * <tt>env</tt> -- Environment | |
27 | # * <tt>override_configurations</tt> (Optional) -- To override the default configuration. | |
28 | # === Examples | |
29 | # obj.set_config(api.config) | |
30 | # obj.set_config(:http_timeout => 30) | |
31 | # obj.set_config(:development) | |
32 | # obj.set_config(:development, :http_timeout => 30) | |
33 | def set_config(env, override_configurations = {}) | |
34 | @config = | |
35 | case env | |
36 | when Config | |
37 | env | |
38 | when Hash | |
39 | begin | |
40 | config.dup.merge!(env) | |
41 | rescue Errno::ENOENT => error | |
42 | Config.new(env) | |
43 | end | |
44 | else | |
45 | Config.config(env, override_configurations) | |
46 | end | |
47 | end | |
48 | | |
49 | alias_method :config=, :set_config | |
50 | end | |
51 | | |
52 | # Config class is used to hold the configurations. | |
53 | # == Examples | |
54 | # # To load configurations from file | |
55 | # Config.load('config/paypal.yml', 'development') | |
56 | # | |
57 | # # Get configuration | |
58 | # Config.config # load default configuration | |
59 | # Config.config(:development) # load development configuration | |
60 | # Config.config(:development, :app_id => "XYZ") # Override configuration | |
61 | # | |
62 | # # Read configuration attributes | |
63 | # config = Config.config | |
64 | # config.username | |
65 | # config.endpoint | |
66 | class Config | |
67 | | |
68 | include Logging | |
69 | include Exceptions | |
70 | | |
71 | attr_accessor :username, :password, :signature, :app_id, :cert_path, | |
72 | :token, :token_secret, :subject, | |
73 | :http_timeout, :http_proxy, | |
74 | :device_ipaddress, :sandbox_email_address, | |
75 | :mode, :endpoint, :merchant_endpoint, :platform_endpoint, :ipn_endpoint, | |
76 | :rest_endpoint, :rest_token_endpoint, :client_id, :client_secret, | |
77 | :openid_endpoint, :openid_redirect_uri, :openid_client_id, :openid_client_secret, | |
78 | :verbose_logging | |
79 | | |
80 | alias_method :end_point=, :endpoint= | |
81 | alias_method :end_point, :endpoint | |
82 | alias_method :platform_end_point=, :platform_endpoint= | |
83 | alias_method :platform_end_point, :platform_endpoint | |
84 | alias_method :merchant_end_point=, :merchant_endpoint= | |
85 | alias_method :merchant_end_point, :merchant_endpoint | |
86 | alias_method :ipn_end_point=, :ipn_endpoint= | |
87 | alias_method :ipn_end_point, :ipn_endpoint | |
88 | alias_method :rest_end_point, :rest_endpoint | |
89 | alias_method :rest_end_point=, :rest_endpoint= | |
90 | alias_method :rest_token_end_point, :rest_token_endpoint | |
91 | alias_method :rest_token_end_point=, :rest_token_endpoint= | |
92 | | |
93 | # Create Config object | |
94 | # === Options(Hash) | |
95 | # * <tt>username</tt> -- Username | |
96 | # * <tt>password</tt> -- Password | |
97 | # * <tt>signature</tt> (Optional if certificate present) -- Signature | |
98 | # * <tt>app_id</tt> -- Application ID | |
99 | # * <tt>cert_path</tt> (Optional if signature present) -- Certificate file path | |
100 | def initialize(options) | |
101 | merge!(options) | |
102 | end | |
103 | | |
104 | def logfile=(filename) | |
105 | logger.warn '`logfile=` is deprecated, Please use `PayPal::SDK::Core::Config.logger = Logger.new(STDERR)`' | |
106 | end | |
107 | | |
108 | def redirect_url=(redirect_url) | |
109 | logger.warn '`redirect_url=` is deprecated.' | |
110 | end | |
111 | | |
112 | def dev_central_url=(dev_central_url) | |
113 | logger.warn '`dev_central_url=` is deprecated.' | |
114 | end | |
115 | | |
116 | def ssl_options | |
117 | @ssl_options ||= {}.freeze | |
118 | end | |
119 | | |
120 | def ssl_options=(options) | |
121 | options = Hash[options.map{|key, value| [key.to_sym, value] }] | |
122 | @ssl_options = ssl_options.merge(options).freeze | |
123 | end | |
124 | | |
125 | def ca_file=(ca_file) | |
126 | logger.warn '`ca_file=` is deprecated, Please configure `ca_file=` under `ssl_options`' | |
127 | self.ssl_options = { :ca_file => ca_file } | |
128 | end | |
129 | | |
130 | def http_verify_mode=(verify_mode) | |
131 | logger.warn '`http_verify_mode=` is deprecated, Please configure `verify_mode=` under `ssl_options`' | |
132 | self.ssl_options = { :verify_mode => verify_mode } | |
133 | end | |
134 | | |
135 | # Override configurations | |
136 | def merge!(options) | |
137 | options.each do |key, value| | |
138 | send("#{key}=", value) | |
139 | end | |
140 | self | |
141 | end | |
142 | | |
143 | # Validate required configuration | |
144 | def required!(*names) | |
145 | names = names.select{|name| send(name).nil? } | |
146 | raise MissingConfig.new("Required configuration(#{names.join(", ")})") if names.any? | |
147 | end | |
148 | | |
149 | class << self | |
150 | | |
151 | @@config_cache = {} | |
152 | | |
153 | # Load configurations from file | |
154 | # === Arguments | |
155 | # * <tt>file_name</tt> -- Configuration file path | |
156 | # * <tt>default_environment</tt> (Optional) -- default environment configuration to load | |
157 | # === Example | |
158 | # Config.load('config/paypal.yml', 'development') | |
159 | def load(file_name, default_env = default_environment) | |
160 | @@config_cache = {} | |
161 | @@configurations = read_configurations(file_name) | |
162 | @@default_environment = default_env | |
163 | config | |
164 | end | |
165 | | |
166 | # Get default environment name | |
167 | def default_environment | |
168 | @@default_environment ||= ENV['PAYPAL_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || "development" | |
169 | end | |
170 | | |
171 | # Set default environment | |
172 | def default_environment=(env) | |
173 | @@default_environment = env.to_s | |
174 | end | |
175 | | |
176 | def configure(options = {}, &block) | |
177 | begin | |
178 | self.config.merge!(options) | |
179 | rescue Errno::ENOENT | |
180 | self.configurations = { default_environment => options } | |
181 | end | |
182 | block.call(self.config) if block | |
183 | self.config | |
184 | end | |
185 | alias_method :set_config, :configure | |
186 | | |
187 | # Create or Load Config object based on given environment and configurations. | |
188 | # === Attributes | |
189 | # * <tt>env</tt> (Optional) -- Environment name | |
190 | # * <tt>override_configuration</tt> (Optional) -- Override the configuration given in file. | |
191 | # === Example | |
192 | # Config.config | |
193 | # Config.config(:development) | |
194 | # Config.config(:development, { :app_id => "XYZ" }) | |
195 | def config(env = default_environment, override_configuration = {}) | |
196 | if env.is_a? Hash | |
197 | override_configuration = env | |
198 | env = default_environment | |
199 | end | |
200 | if override_configuration.nil? or override_configuration.empty? | |
201 | default_config(env) | |
202 | else | |
203 | default_config(env).dup.merge!(override_configuration) | |
204 | end | |
205 | end | |
206 | | |
207 | def default_config(env = nil) | |
208 | env = (env || default_environment).to_s | |
209 | if configurations[env] | |
210 | @@config_cache[env] ||= new(configurations[env]) | |
211 | else | |
212 | raise Exceptions::MissingConfig.new("Configuration[#{env}] NotFound") | |
213 | end | |
214 | end | |
215 | | |
216 | # Set logger | |
217 | def logger=(logger) | |
218 | Logging.logger = logger | |
219 | end | |
220 | | |
221 | # Get logger | |
222 | def logger | |
223 | if @@configurations[:mode] == 'live' and Logging.logger.level == Logger::DEBUG | |
224 | Logging.logger.warn "DEBUG log level not allowed in live mode for security of confidential information. Changing log level to INFO..." | |
225 | Logging.logger.level = Logger::INFO | |
226 | end | |
227 | Logging.logger | |
228 | end | |
229 | | |
230 | # Get raw configurations in Hash format. | |
231 | def configurations | |
232 | @@configurations ||= read_configurations | |
233 | end | |
234 | | |
235 | # Set configuration | |
236 | def configurations=(configs) | |
237 | @@config_cache = {} | |
238 | @@configurations = configs && Hash[configs.map{|k,v| [k.to_s, v] }] | |
239 | end | |
240 | | |
241 | private | |
242 | # Read configurations from the given file name | |
243 | # === Arguments | |
244 | # * <tt>file_name</tt> (Optional) -- Configuration file path | |
245 | def read_configurations(file_name = "config/paypal.yml") | |
246 | erb = ERB.new(File.read(file_name)) | |
247 | erb.filename = file_name | |
248 | YAML.load(erb.result) | |
249 | end | |
250 | | |
251 | end | |
252 | end | |
253 | end | |
254 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module Core | |
4 | module Credential | |
5 | autoload :Base, "paypal-sdk/core/credential/base" | |
6 | autoload :Certificate, "paypal-sdk/core/credential/certificate" | |
7 | autoload :Signature, "paypal-sdk/core/credential/signature" | |
8 | | |
9 | module ThirdParty | |
10 | autoload :Token, "paypal-sdk/core/credential/third_party/token" | |
11 | autoload :Subject, "paypal-sdk/core/credential/third_party/subject" | |
12 | end | |
13 | end | |
14 | end | |
15 | end | |
16 | end | |
17 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential/base.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Credential | |
3 | | |
4 | # Base credential Class for authentication | |
5 | class Base | |
6 | attr_accessor :username, :password, :app_id, :device_ipaddress, :sandbox_email_address | |
7 | | |
8 | # Initialize authentication configurations | |
9 | # === Arguments | |
10 | # * <tt>config</tt> -- Configuration object | |
11 | def initialize(config) | |
12 | self.username = config.username | |
13 | self.password = config.password | |
14 | self.app_id = config.app_id | |
15 | self.device_ipaddress = config.device_ipaddress | |
16 | self.sandbox_email_address = config.sandbox_email_address | |
17 | end | |
18 | | |
19 | # Return credential properties | |
20 | def properties | |
21 | { :username => username, :password => password, :app_id => app_id, | |
22 | :device_ipaddress => device_ipaddress, :sandbox_email_address => sandbox_email_address } | |
23 | end | |
24 | | |
25 | end | |
26 | end | |
27 | end | |
28 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential/certificate.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Credential | |
3 | | |
4 | # Certificate class for SSL Certificate authentication | |
5 | class Certificate < Base | |
6 | | |
7 | attr_reader :cert_path | |
8 | | |
9 | def initialize(config) | |
10 | super | |
11 | @cert_path = config.cert_path | |
12 | end | |
13 | | |
14 | # Return SSL certificate | |
15 | def cert | |
16 | @cert ||= OpenSSL::X509::Certificate.new(cert_content) | |
17 | end | |
18 | | |
19 | # Return SSL certificate key | |
20 | def key | |
21 | @key = OpenSSL::PKey::RSA.new(cert_content) | |
22 | end | |
23 | | |
24 | private | |
25 | # Return certificate content from the configured file. | |
26 | def cert_content | |
27 | @cert_content ||= File.read(cert_path) | |
28 | end | |
29 | | |
30 | end | |
31 | end | |
32 | end | |
33 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential/signature.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Credential | |
3 | class Signature < Base | |
4 | | |
5 | attr_accessor :signature | |
6 | | |
7 | # Initialize configuration | |
8 | # === Argument | |
9 | # * <tt>config</tt> -- Configuration object | |
10 | def initialize(config) | |
11 | super | |
12 | self.signature = config.signature | |
13 | end | |
14 | | |
15 | # Return properties for authentication | |
16 | def properties | |
17 | super.merge({ :signature => signature }) | |
18 | end | |
19 | | |
20 | end | |
21 | end | |
22 | end | |
23 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential/third_party/subject.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Credential | |
3 | module ThirdParty | |
4 | class Subject | |
5 | | |
6 | attr_accessor :subject, :credential | |
7 | | |
8 | # Initialize configuration | |
9 | # === Arguments | |
10 | # * <tt>credential</tt> -- Credential object | |
11 | # * <tt>config</tt> -- Configuration object | |
12 | def initialize(credential, config) | |
13 | @credential = credential | |
14 | @subject = config.subject | |
15 | end | |
16 | | |
17 | # Return properties for authentication. | |
18 | def properties | |
19 | credential.properties.merge( :subject => subject ) | |
20 | end | |
21 | | |
22 | end | |
23 | end | |
24 | end | |
25 | end | |
26 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/credential/third_party/token.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Credential | |
3 | module ThirdParty | |
4 | class Token | |
5 | | |
6 | attr_accessor :token, :token_secret, :credential, :url | |
7 | | |
8 | # Initialize Token credentials | |
9 | # === Arguments | |
10 | # * <tt>credential</tt> -- Credential Object | |
11 | # * <tt>config</tt> -- Configuration object | |
12 | # * <tt>url</tt> -- Request url | |
13 | def initialize(credential, config, url) | |
14 | @credential = credential | |
15 | @token = config.token | |
16 | @token_secret = config.token_secret | |
17 | @url = url | |
18 | end | |
19 | | |
20 | RemoveProperties = [ :username, :password, :signature ] | |
21 | | |
22 | # Return credential properties for authentication. | |
23 | def properties | |
24 | credential_properties = credential.properties | |
25 | credential_properties.delete_if{|k,v| RemoveProperties.include? k } | |
26 | credential_properties.merge( :authorization => oauth_authentication ) | |
27 | end | |
28 | | |
29 | private | |
30 | # Return OAuth authentication string. | |
31 | def oauth_authentication | |
32 | Util::OauthSignature.new(credential.username, credential.password, token, token_secret, url). | |
33 | authorization_string | |
34 | end | |
35 | | |
36 | end | |
37 | end | |
38 | end | |
39 | end | |
40 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/exceptions.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'json' | |
2 | require 'pp' | |
3 | | |
4 | module PayPal::SDK::Core | |
5 | module Exceptions | |
6 | class ConnectionError < StandardError # :nodoc: | |
7 | attr_reader :response | |
8 | | |
9 | def initialize(response, message = nil) | |
10 | @response = response | |
11 | @message = message | |
12 | end | |
13 | | |
14 | def to_s | |
15 | begin | |
16 | response_body = JSON.parse(response.body) | |
17 | debug_id = response["paypal-debug-id"] | |
18 | debug_id = response["correlation-id"] if debug_id.to_s == '' | |
19 | debug_id = response_body["debug_id"] if debug_id.to_s == '' | |
20 | rescue | |
21 | end | |
22 | message = "Failed." | |
23 | message << " Response code = #{response.code}." if response.respond_to?(:code) | |
24 | message << " Response message = #{response.message}." if response.respond_to?(:message) | |
25 | message << " Response debug ID = #{debug_id}." if debug_id | |
26 | message | |
27 | end | |
28 | end | |
29 | | |
30 | # Raised when a Timeout::Error occurs. | |
31 | class TimeoutError < ConnectionError | |
32 | def initialize(message) | |
33 | @message = message | |
34 | end | |
35 | def to_s; @message ;end | |
36 | end | |
37 | | |
38 | # Raised when a OpenSSL::SSL::SSLError occurs. | |
39 | class SSLError < ConnectionError | |
40 | def initialize(message) | |
41 | @message = message | |
42 | end | |
43 | def to_s; @message ;end | |
44 | end | |
45 | | |
46 | # 3xx Redirection | |
47 | class Redirection < ConnectionError # :nodoc: | |
48 | def to_s | |
49 | response['Location'] ? "#{super} => #{response['Location']}" : super | |
50 | end | |
51 | end | |
52 | | |
53 | class MissingParam < ArgumentError # :nodoc: | |
54 | end | |
55 | | |
56 | class MissingConfig < StandardError # :nodoc: | |
57 | end | |
58 | | |
59 | # 4xx Client Error | |
60 | class ClientError < ConnectionError # :nodoc: | |
61 | end | |
62 | | |
63 | # 400 Bad Request | |
64 | class BadRequest < ClientError # :nodoc: | |
65 | end | |
66 | | |
67 | # 401 Unauthorized | |
68 | class UnauthorizedAccess < ClientError # :nodoc: | |
69 | end | |
70 | | |
71 | # 403 Forbidden | |
72 | class ForbiddenAccess < ClientError # :nodoc: | |
73 | end | |
74 | | |
75 | # 404 Not Found | |
76 | class ResourceNotFound < ClientError # :nodoc: | |
77 | end | |
78 | | |
79 | # 409 Conflict | |
80 | class ResourceConflict < ClientError # :nodoc: | |
81 | end | |
82 | | |
83 | # 410 Gone | |
84 | class ResourceGone < ClientError # :nodoc: | |
85 | end | |
86 | | |
87 | # 422 Unprocessable Entity | |
88 | class ResourceInvalid < ClientError # :nodoc: | |
89 | end | |
90 | | |
91 | # 5xx Server Error | |
92 | class ServerError < ConnectionError # :nodoc: | |
93 | end | |
94 | | |
95 | # 405 Method Not Allowed | |
96 | class MethodNotAllowed < ClientError # :nodoc: | |
97 | def allowed_methods | |
98 | @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym } | |
99 | end | |
100 | end | |
101 | | |
102 | # API error: returned as 200 + "error" key in response. | |
103 | class UnsuccessfulApiCall < RuntimeError | |
104 | attr_reader :api_error | |
105 | | |
106 | def initialize(api_error) | |
107 | super(api_error['message']) | |
108 | @api_error = api_error | |
109 | end | |
110 | end | |
111 | end | |
112 | end | |
113 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/logging.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'logger' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | | |
5 | # Include Logging module to provide logger functionality. | |
6 | # == Configure logger | |
7 | # Logging.logger = Logger.new(STDERR) | |
8 | # | |
9 | # == Example | |
10 | # include Logger | |
11 | # logger.info "Debug message" | |
12 | module Logging | |
13 | | |
14 | # Get logger object | |
15 | def logger | |
16 | @logger ||= Logging.logger | |
17 | end | |
18 | | |
19 | def log_event(message, &block) | |
20 | start_time = Time.now | |
21 | block.call | |
22 | ensure | |
23 | logger.info sprintf("[%.3fs] %s", Time.now - start_time, message) | |
24 | end | |
25 | | |
26 | class << self | |
27 | | |
28 | # Get or Create configured logger based on the default environment configuration | |
29 | def logger | |
30 | @logger ||= Logger.new(STDERR) | |
31 | end | |
32 | | |
33 | # Set logger directly and clear the loggers cache. | |
34 | # === Attributes | |
35 | # * <tt>logger</tt> -- Logger object | |
36 | # === Example | |
37 | # Logging.logger = Logger.new(STDERR) | |
38 | def logger=(logger) | |
39 | @logger = logger | |
40 | if Config.config.mode.eql? 'live' and @logger.level == Logger::DEBUG | |
41 | @logger.warn "DEBUG log level not allowed in sandbox mode for security of confidential information. Changing log level to INFO..." | |
42 | @logger.level = Logger::INFO | |
43 | end | |
44 | end | |
45 | | |
46 | end | |
47 | end | |
48 | | |
49 | end | |
50 | | |
51 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect.rb: | |
-------------------------------------------------------------------------------- | |
1 | | |
2 | module PayPal::SDK | |
3 | module Core | |
4 | module OpenIDConnect | |
5 | autoload :API, "paypal-sdk/core/openid_connect/api" | |
6 | autoload :SetAPI, "paypal-sdk/core/openid_connect/set_api" | |
7 | autoload :GetAPI, "paypal-sdk/core/openid_connect/get_api" | |
8 | autoload :RequestDataType, "paypal-sdk/core/openid_connect/request_data_type" | |
9 | autoload :DataTypes, "paypal-sdk/core/openid_connect/data_types" | |
10 | | |
11 | include DataTypes | |
12 | | |
13 | class << self | |
14 | def api | |
15 | RequestDataType.api | |
16 | end | |
17 | | |
18 | def set_config(*args) | |
19 | RequestDataType.set_config(*args) | |
20 | end | |
21 | alias_method :config=, :set_config | |
22 | | |
23 | AUTHORIZATION_URL = "paypal.com/signin/authorize" | |
24 | ENDSESSION_URL = "paypal.com/webapps/auth/protocol/openidconnect/v1/endsession" | |
25 | DEFAULT_SCOPE = "openid" | |
26 | | |
27 | def authorize_url(params = {}) | |
28 | uri = URI(url_for_mode(AUTHORIZATION_URL)) | |
29 | uri.query = api.encode_www_form({ | |
30 | :response_type => "code", | |
31 | :scope => DEFAULT_SCOPE, | |
32 | :client_id => RequestDataType.client_id, | |
33 | :redirect_uri => api.config.openid_redirect_uri | |
34 | }.merge(params)) | |
35 | uri.to_s | |
36 | end | |
37 | | |
38 | def logout_url(params = {}) | |
39 | uri = URI(url_for_mode(ENDSESSION_URL)) | |
40 | uri.query = api.encode_www_form({ | |
41 | :logout => "true", | |
42 | :redirect_uri => api.config.openid_redirect_uri | |
43 | }.merge(params)) | |
44 | uri.to_s | |
45 | end | |
46 | | |
47 | private | |
48 | | |
49 | def url_for_mode(url) | |
50 | "https://www.#{"sandbox." if api.api_mode == :sandbox}#{url}" | |
51 | end | |
52 | end | |
53 | | |
54 | module DataTypes | |
55 | class Tokeninfo < Base | |
56 | include RequestDataType | |
57 | PATH = "v1/identity/openidconnect/tokenservice" | |
58 | FP_PATH = "v1/oauth2/token" | |
59 | | |
60 | class << self | |
61 | | |
62 | def basic_auth_header(options) | |
63 | credentials = options[:client_id].to_s + ":" + options[:client_secret].to_s | |
64 | encoded = Base64.encode64(credentials.force_encoding('UTF-8')).gsub!(/\n/, "") | |
65 | "Basic #{encoded}" | |
66 | end | |
67 | | |
68 | def create_from_authorization_code(options, http_header = {}) | |
69 | options = { :code => options } if options.is_a? String | |
70 | options = options.merge( :grant_type => "authorization_code" ) | |
71 | Tokeninfo.new(api.post(PATH, with_credentials(options), http_header)) | |
72 | end | |
73 | alias_method :create, :create_from_authorization_code | |
74 | | |
75 | def create_from_refresh_token(options, http_header = {}) | |
76 | options = { :refresh_token => options } if options.is_a? String | |
77 | options = options.merge( :grant_type => "refresh_token" ) | |
78 | http_header = http_header.merge( { "Content-Type" => "application/x-www-form-urlencoded", "Authorization" => basic_auth_header(with_credentials(options)) } ) | |
79 | Tokeninfo.new(api.post(PATH, options, http_header)) | |
80 | end | |
81 | alias_method :refresh, :create_from_refresh_token | |
82 | | |
83 | def create_from_future_payment_auth_code(options, http_header = {}) | |
84 | options = { :code => options } if options.is_a? String | |
85 | options = options.merge( { :grant_type => "authorization_code", :response_type => "token", :redirect_uri => "urn:ietf:wg:oauth:2.0:oob" } ) | |
86 | http_header = http_header.merge( { "Content-Type" => "application/x-www-form-urlencoded", "Authorization" => basic_auth_header(with_credentials(options)) } ) | |
87 | Tokeninfo.new(api.post(FP_PATH, options, http_header)) | |
88 | end | |
89 | alias_method :token_hash, :create_from_future_payment_auth_code | |
90 | alias_method :create_fp, :create_from_future_payment_auth_code | |
91 | | |
92 | def with_credentials(options = {}) | |
93 | options = options.dup | |
94 | [ :client_id, :client_secret ].each do |key| | |
95 | options[key] = self.send(key) unless options[key] or options[key.to_s] | |
96 | end | |
97 | options | |
98 | end | |
99 | | |
100 | def authorize_url(options = {}) | |
101 | OpenIDConnect.authorize_url(options) | |
102 | end | |
103 | end | |
104 | | |
105 | def refresh(options = {}) | |
106 | tokeninfo = self.class.refresh({ | |
107 | :refresh_token => self.refresh_token}.merge(options)) | |
108 | self.merge!(tokeninfo.to_hash) | |
109 | end | |
110 | | |
111 | def userinfo(options = {}) | |
112 | Userinfo.get({ :access_token => self.access_token }.merge(options)) | |
113 | end | |
114 | | |
115 | def logout_url(options = {}) | |
116 | OpenIDConnect.logout_url({ :id_token => self.id_token }.merge(options)) | |
117 | end | |
118 | | |
119 | end | |
120 | | |
121 | class Userinfo < Base | |
122 | include RequestDataType | |
123 | PATH = "v1/identity/openidconnect/userinfo" | |
124 | | |
125 | class << self | |
126 | def get_userinfo(options = {}, http_header = {}) | |
127 | options = { :access_token => options } if options.is_a? String | |
128 | options = options.merge( :schema => "openid" ) unless options[:schema] or options["schema"] | |
129 | Userinfo.new(api.post(PATH, options, http_header)) | |
130 | end | |
131 | alias_method :get, :get_userinfo | |
132 | end | |
133 | end | |
134 | end | |
135 | end | |
136 | end | |
137 | | |
138 | # Alias for the Core::OpenIDConnect constant | |
139 | OpenIDConnect = Core::OpenIDConnect | |
140 | end | |
141 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect/api.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'multi_json' | |
2 | require 'paypal-sdk/rest/version' | |
3 | | |
4 | module PayPal::SDK | |
5 | module Core | |
6 | module OpenIDConnect | |
7 | class API < Core::API::Base | |
8 | | |
9 | DEFAULT_OPENID_ENDPOINT = { | |
10 | :sandbox => "https://api.sandbox.paypal.com", | |
11 | :live => "https://api.paypal.com" } | |
12 | | |
13 | def initialize(environment = nil, options = {}) | |
14 | super("", environment, options) | |
15 | end | |
16 | | |
17 | def service_endpoint | |
18 | self.config.openid_endpoint || DEFAULT_OPENID_ENDPOINT[self.api_mode] || DEFAULT_OPENID_ENDPOINT[:sandbox] | |
19 | end | |
20 | | |
21 | def format_request(payload) | |
22 | payload[:uri].path = url_join(payload[:uri].path, payload[:action]) | |
23 | payload[:body] = encode_www_form(payload[:params]) if payload[:params] | |
24 | payload[:header] = {"Content-Type" => "application/x-www-form-urlencoded" }.merge(payload[:header]) | |
25 | payload | |
26 | end | |
27 | | |
28 | def format_response(payload) | |
29 | payload[:data] = | |
30 | if payload[:response].code >= "200" and payload[:response].code <= "299" | |
31 | MultiJson.load(payload[:response].body) | |
32 | elsif payload[:response].content_type == "application/json" | |
33 | { "error" => MultiJson.load(payload[:response].body) } | |
34 | else | |
35 | { "error" => { "name" => payload[:response].code, "message" => payload[:response].message, | |
36 | "developer_msg" => payload[:response] } } | |
37 | end | |
38 | payload | |
39 | end | |
40 | | |
41 | class << self | |
42 | def user_agent | |
43 | @user_agent ||= "PayPalSDK/openid-connect-ruby #{PayPal::SDK::REST::VERSION} (#{sdk_library_details})" | |
44 | end | |
45 | end | |
46 | | |
47 | end | |
48 | end | |
49 | end | |
50 | end | |
51 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect/data_types.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-core' | |
2 | | |
3 | module PayPal::SDK::Core | |
4 | module OpenIDConnect | |
5 | module DataTypes | |
6 | class Base < PayPal::SDK::Core::API::DataTypes::Base | |
7 | end | |
8 | | |
9 | class Address < Base | |
10 | def self.load_members | |
11 | object_of :street_address, String | |
12 | object_of :locality, String | |
13 | object_of :region, String | |
14 | object_of :postal_code, String | |
15 | object_of :country, String | |
16 | end | |
17 | end | |
18 | | |
19 | class Userinfo < Base | |
20 | def self.load_members | |
21 | object_of :user_id, String | |
22 | object_of :sub, String | |
23 | object_of :name, String | |
24 | object_of :given_name, String | |
25 | object_of :family_name, String | |
26 | object_of :middle_name, String | |
27 | object_of :picture, String | |
28 | object_of :email, String | |
29 | object_of :email_verified, Boolean | |
30 | object_of :gender, String | |
31 | object_of :birthday, String | |
32 | object_of :zoneinfo, String | |
33 | object_of :locale, String | |
34 | object_of :language, String | |
35 | object_of :verified, Boolean | |
36 | object_of :phone_number, String | |
37 | object_of :address, Address | |
38 | object_of :verified_account, Boolean | |
39 | object_of :account_type, String | |
40 | object_of :account_creation_date, String | |
41 | object_of :age_range, String | |
42 | object_of :payer_id, String | |
43 | end | |
44 | end | |
45 | | |
46 | class Tokeninfo < Base | |
47 | def self.load_members | |
48 | object_of :scope, String | |
49 | object_of :access_token, String | |
50 | object_of :refresh_token, String | |
51 | object_of :token_type, String | |
52 | object_of :id_token, String | |
53 | object_of :expires_in, Integer | |
54 | end | |
55 | end | |
56 | | |
57 | class Error < Base | |
58 | def self.load_members | |
59 | object_of :error, String | |
60 | object_of :error_description, String | |
61 | object_of :error_uri, String | |
62 | end | |
63 | end | |
64 | | |
65 | | |
66 | constants.each do |data_type_klass| | |
67 | data_type_klass = const_get(data_type_klass) | |
68 | data_type_klass.load_members if defined? data_type_klass.load_members | |
69 | end | |
70 | | |
71 | end | |
72 | end | |
73 | end | |
74 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect/get_api.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK | |
2 | module Core | |
3 | module OpenIDConnect | |
4 | module GetAPI | |
5 | # Get API object | |
6 | # === Example | |
7 | # Payment.api | |
8 | # payment.api | |
9 | def api | |
10 | @api || parent_api | |
11 | end | |
12 | | |
13 | # Parent API object | |
14 | def parent_api | |
15 | superclass.respond_to?(:api) ? superclass.api : RequestDataType.api | |
16 | end | |
17 | | |
18 | def client_id | |
19 | api.config.openid_client_id || api.config.client_id | |
20 | end | |
21 | | |
22 | def client_secret | |
23 | api.config.openid_client_secret || api.config.client_secret | |
24 | end | |
25 | end | |
26 | end | |
27 | end | |
28 | end | |
29 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect/request_data_type.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK | |
2 | module Core | |
3 | module OpenIDConnect | |
4 | module RequestDataType | |
5 | | |
6 | # Get a local API object or Class level API object | |
7 | def api | |
8 | @api || self.class.api | |
9 | end | |
10 | | |
11 | class << self | |
12 | # Global API object | |
13 | # === Example | |
14 | # RequestDataType.api | |
15 | def api | |
16 | @api ||= API.new({}) | |
17 | end | |
18 | | |
19 | def client_id | |
20 | api.config.openid_client_id || api.config.client_id | |
21 | end | |
22 | | |
23 | def client_secret | |
24 | api.config.openid_client_secret || api.config.client_secret | |
25 | end | |
26 | | |
27 | # Setter for RequestDataType.api | |
28 | # === Example | |
29 | # RequestDataType.set_config(..) | |
30 | include SetAPI | |
31 | | |
32 | # Configure depended module, when RequestDataType is include. | |
33 | # === Example | |
34 | # class Payment < DataTypes | |
35 | # include RequestDataType | |
36 | # end | |
37 | # Payment.set_config(..) | |
38 | # payment.set_config(..) | |
39 | # Payment.api | |
40 | # payment.api | |
41 | def included(klass) | |
42 | klass.class_eval do | |
43 | extend GetAPI | |
44 | extend SetAPI | |
45 | include SetAPI | |
46 | end | |
47 | end | |
48 | end | |
49 | end | |
50 | end | |
51 | end | |
52 | end | |
53 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/openid_connect/set_api.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK | |
2 | module Core | |
3 | module OpenIDConnect | |
4 | module SetAPI | |
5 | # Set new api | |
6 | # === Examples | |
7 | # payment.set_config(:development) | |
8 | # payment.set_config(:client_id => "XYZ", :client_secret => "SECRET") | |
9 | # payment.set_config | |
10 | # payment.api = API.new(:development) | |
11 | def set_config(*args) | |
12 | if args[0].is_a?(API) | |
13 | @api = args[0] | |
14 | else | |
15 | @api ||= API.new({}) | |
16 | @api.set_config(*args) # Just override the configuration and Not | |
17 | @api | |
18 | end | |
19 | end | |
20 | alias_method :config=, :set_config | |
21 | alias_method :set_api, :set_config | |
22 | alias_method :api=, :set_config | |
23 | | |
24 | # Override client id | |
25 | def client_id=(client_id) | |
26 | set_config(:client_id => client_id) | |
27 | end | |
28 | | |
29 | # Override client secret | |
30 | def client_secret=(client_secret) | |
31 | set_config(:client_secret => client_secret) | |
32 | end | |
33 | end | |
34 | end | |
35 | end | |
36 | end | |
37 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/util.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module Core | |
4 | module Util | |
5 | autoload :OauthSignature, "paypal-sdk/core/util/oauth_signature" | |
6 | autoload :OrderedHash, "paypal-sdk/core/util/ordered_hash" | |
7 | autoload :HTTPHelper, "paypal-sdk/core/util/http_helper" | |
8 | end | |
9 | end | |
10 | end | |
11 | end | |
12 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/util/http_helper.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'net/https' | |
2 | require 'uri' | |
3 | require 'cgi' | |
4 | | |
5 | module PayPal::SDK::Core | |
6 | module Util | |
7 | module HTTPHelper | |
8 | | |
9 | include Configuration | |
10 | include Logging | |
11 | include Authentication | |
12 | include Exceptions | |
13 | | |
14 | # Create HTTP connection based on given service name or configured end point | |
15 | def create_http_connection(uri) | |
16 | new_http(uri).tap do |http| | |
17 | if config.http_timeout | |
18 | http.open_timeout = config.http_timeout | |
19 | http.read_timeout = config.http_timeout | |
20 | end | |
21 | configure_ssl(http) if uri.scheme == "https" | |
22 | end | |
23 | end | |
24 | | |
25 | # New raw HTTP object | |
26 | def new_http(uri) | |
27 | if config.http_proxy | |
28 | proxy = URI.parse(config.http_proxy) | |
29 | Net::HTTP.new(uri.host, uri.port, proxy.host, proxy.port, proxy.user, proxy.password) | |
30 | else | |
31 | Net::HTTP.new(uri.host, uri.port) | |
32 | end | |
33 | end | |
34 | | |
35 | # Default ca file | |
36 | def default_ca_file | |
37 | File.expand_path("../../../../../data/paypal.crt", __FILE__) | |
38 | end | |
39 | | |
40 | # Apply ssl configuration to http object | |
41 | def configure_ssl(http) | |
42 | http.tap do |https| | |
43 | https.use_ssl = true | |
44 | https.ca_file = default_ca_file | |
45 | https.verify_mode = OpenSSL::SSL::VERIFY_PEER | |
46 | begin | |
47 | https.ssl_version = :TLSv1_2 | |
48 | rescue => error | |
49 | logger.warn("WARNING: Your system does not support TLSv1.2. Per PCI Security Council mandate (https://github.com/paypal/TLS-update), you MUST update to latest security library.") | |
50 | end | |
51 | config.ssl_options.each do |key, value| | |
52 | http.send("#{key}=", value) | |
53 | end | |
54 | add_certificate(https) | |
55 | end | |
56 | end | |
57 | | |
58 | # Join url | |
59 | def url_join(path, action) | |
60 | path.sub(/\/?$/, "/#{action}") | |
61 | end | |
62 | | |
63 | # Make Http call | |
64 | # * payload - Hash(:http, :method, :uri, :body, :header) | |
65 | def http_call(payload) | |
66 | response = | |
67 | log_http_call(payload) do | |
68 | http = payload[:http] || create_http_connection(payload[:uri]) | |
69 | http.start do |session| | |
70 | if [ :get, :delete, :head ].include? payload[:method] | |
71 | session.send(payload[:method], payload[:uri].request_uri, payload[:header]) | |
72 | else | |
73 | session.send(payload[:method], payload[:uri].request_uri, payload[:body], payload[:header]) | |
74 | end | |
75 | end | |
76 | end | |
77 | | |
78 | handle_response(response) | |
79 | end | |
80 | | |
81 | # Log Http call | |
82 | # * payload - Hash(:http, :method, :uri, :body, :header) | |
83 | def log_http_call(payload) | |
84 | logger.info "Request[#{payload[:method]}]: #{payload[:uri].to_s}" | |
85 | | |
86 | logger.debug "Request.body=#{payload[:body]}\trequest.header=#{payload[:header]}" | |
87 | | |
88 | start_time = Time.now | |
89 | response = yield | |
90 | logger.info sprintf("Response[%s]: %s, Duration: %.3fs", response.code, | |
91 | response.message, Time.now - start_time) | |
92 | | |
93 | logger.add( | |
94 | response_details_log_level(response), | |
95 | "Response.body=#{response.body}\tResponse.header=#{response.to_hash}" | |
96 | ) | |
97 | | |
98 | response | |
99 | end | |
100 | | |
101 | # Generate header based on given header keys and properties | |
102 | # === Arguments | |
103 | # * <tt>header_keys</tt> -- List of Header keys for the properties | |
104 | # * <tt>properties</tt> -- properties | |
105 | # === Return | |
106 | # Hash with header as key property as value | |
107 | # === Example | |
108 | # map_header_value( { :username => "X-PAYPAL-USERNAME"}, { :username => "guest" }) | |
109 | # # Return: { "X-PAYPAL-USERNAME" => "guest" } | |
110 | def map_header_value(header_keys, properties) | |
111 | header = {} | |
112 | properties.each do |key, value| | |
113 | key = header_keys[key] | |
114 | header[key] = value.to_s if key and value | |
115 | end | |
116 | header | |
117 | end | |
118 | | |
119 | def encode_www_form(hash) | |
120 | if defined? URI.encode_www_form | |
121 | URI.encode_www_form(hash) | |
122 | else | |
123 | hash.map{|key, value| "#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}" }.join("&") | |
124 | end | |
125 | end | |
126 | | |
127 | # Handles response and error codes from the remote service. | |
128 | def handle_response(response) | |
129 | case response.code.to_i | |
130 | when 301, 302, 303, 307 | |
131 | raise(Redirection.new(response)) | |
132 | when 200...400 | |
133 | response | |
134 | when 400 | |
135 | raise(BadRequest.new(response)) | |
136 | when 401 | |
137 | raise(UnauthorizedAccess.new(response)) | |
138 | when 403 | |
139 | raise(ForbiddenAccess.new(response)) | |
140 | when 404 | |
141 | raise(ResourceNotFound.new(response)) | |
142 | when 405 | |
143 | raise(MethodNotAllowed.new(response)) | |
144 | when 409 | |
145 | raise(ResourceConflict.new(response)) | |
146 | when 410 | |
147 | raise(ResourceGone.new(response)) | |
148 | when 422 | |
149 | raise(ResourceInvalid.new(response)) | |
150 | when 401...500 | |
151 | raise(ClientError.new(response)) | |
152 | when 500...600 | |
153 | raise(ServerError.new(response)) | |
154 | else | |
155 | raise(ConnectionError.new(response, "Unknown response code: #{response.code}")) | |
156 | end | |
157 | end | |
158 | | |
159 | private | |
160 | | |
161 | def response_details_log_level(response) | |
162 | if (400...600).cover?(response.code.to_i) | |
163 | Logger::WARN | |
164 | else | |
165 | Logger::DEBUG | |
166 | end | |
167 | end | |
168 | | |
169 | end | |
170 | end | |
171 | end | |
172 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/util/oauth_signature.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'uri' | |
2 | require 'cgi' | |
3 | require 'openssl' | |
4 | require 'base64' | |
5 | | |
6 | module PayPal::SDK::Core | |
7 | module Util | |
8 | class OauthSignature | |
9 | attr_accessor :username, :password, :token, :token_secret, :url, :timestamp | |
10 | | |
11 | def initialize(username, password, token, token_secret, url, timestamp = nil) | |
12 | @username = username | |
13 | @password = password | |
14 | @token = token | |
15 | @token_secret = token_secret | |
16 | @url = url | |
17 | @timestamp = timestamp || Time.now.to_i.to_s | |
18 | end | |
19 | | |
20 | def authorization_string | |
21 | signature = oauth_signature | |
22 | "token=#{token},signature=#{signature},timestamp=#{timestamp}" | |
23 | end | |
24 | | |
25 | def oauth_signature | |
26 | key = [ | |
27 | paypal_encode(password), | |
28 | paypal_encode(token_secret), | |
29 | ].join("&").gsub(/%[0-9A-F][0-9A-F]/, &:downcase ) | |
30 | | |
31 | digest = OpenSSL::HMAC.digest('sha1', key, base_string) | |
32 | Base64.encode64(digest).chomp | |
33 | end | |
34 | | |
35 | def base_string | |
36 | params = { | |
37 | "oauth_consumer_key" => username, | |
38 | "oauth_version" => "1.0", | |
39 | "oauth_signature_method" => "HMAC-SHA1", | |
40 | "oauth_token" => token, | |
41 | "oauth_timestamp" => timestamp, | |
42 | } | |
43 | sorted_query_string = params.sort.map{|v| v.join("=") }.join("&") | |
44 | | |
45 | base = [ | |
46 | "POST", | |
47 | paypal_encode(url), | |
48 | paypal_encode(sorted_query_string) | |
49 | ].join("&") | |
50 | base = base.gsub(/%[0-9A-F][0-9A-F]/, &:downcase ) | |
51 | end | |
52 | | |
53 | # The PayPalURLEncoder java class percent encodes everything other than 'a-zA-Z0-9 _'. | |
54 | # Then it converts ' ' to '+'. | |
55 | # Ruby's CGI.encode takes care of the ' ' and '*' to satisfy PayPal | |
56 | # (but beware, URI.encode percent encodes spaces, and does nothing with '*'). | |
57 | # Finally, CGI.encode does not encode '.-', which we need to do here. | |
58 | def paypal_encode str | |
59 | CGI.escape(str.to_s).gsub('.', '%2E').gsub('-', '%2D') | |
60 | end | |
61 | end | |
62 | end | |
63 | end | |
64 | | |
65 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/core/util/ordered_hash.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::Core | |
2 | module Util | |
3 | class OrderedHash < ::Hash #:nodoc: | |
4 | | |
5 | def to_yaml_type | |
6 | "!tag:yaml.org,2002:map" | |
7 | end | |
8 | | |
9 | # Hash is ordered in Ruby 1.9! | |
10 | if RUBY_VERSION < '1.9' | |
11 | | |
12 | # In MRI the Hash class is core and written in C. In particular, methods are | |
13 | # programmed with explicit C function calls and polymorphism is not honored. | |
14 | # | |
15 | # For example, []= is crucial in this implementation to maintain the @keys | |
16 | # array but hash.c invokes rb_hash_aset() originally. This prevents method | |
17 | # reuse through inheritance and forces us to reimplement stuff. | |
18 | # | |
19 | # For instance, we cannot use the inherited #merge! because albeit the algorithm | |
20 | # itself would work, our []= is not being called at all by the C code. | |
21 | | |
22 | def initialize(*args, &block) | |
23 | super | |
24 | @keys = [] | |
25 | end | |
26 | | |
27 | def self.[](*args) | |
28 | ordered_hash = new | |
29 | | |
30 | if (args.length == 1 && args.first.is_a?(Array)) | |
31 | args.first.each do |key_value_pair| | |
32 | next unless (key_value_pair.is_a?(Array)) | |
33 | ordered_hash[key_value_pair[0]] = key_value_pair[1] | |
34 | end | |
35 | | |
36 | return ordered_hash | |
37 | end | |
38 | | |
39 | unless (args.size % 2 == 0) | |
40 | raise ArgumentError.new("odd number of arguments for Hash") | |
41 | end | |
42 | | |
43 | args.each_with_index do |val, ind| | |
44 | next if (ind % 2 != 0) | |
45 | ordered_hash[val] = args[ind + 1] | |
46 | end | |
47 | | |
48 | ordered_hash | |
49 | end | |
50 | | |
51 | def initialize_copy(other) | |
52 | super | |
53 | # make a deep copy of keys | |
54 | @keys = other.keys | |
55 | end | |
56 | | |
57 | def []=(key, value) | |
58 | @keys << key if !has_key?(key) | |
59 | super | |
60 | end | |
61 | | |
62 | def delete(key) | |
63 | if has_key? key | |
64 | index = @keys.index(key) | |
65 | @keys.delete_at index | |
66 | end | |
67 | super | |
68 | end | |
69 | | |
70 | def delete_if | |
71 | super | |
72 | sync_keys! | |
73 | self | |
74 | end | |
75 | | |
76 | def reject! | |
77 | super | |
78 | sync_keys! | |
79 | self | |
80 | end | |
81 | | |
82 | def reject(&block) | |
83 | dup.reject!(&block) | |
84 | end | |
85 | | |
86 | def keys | |
87 | @keys.dup | |
88 | end | |
89 | | |
90 | def values | |
91 | @keys.collect { |key| self[key] } | |
92 | end | |
93 | | |
94 | def to_hash | |
95 | self | |
96 | end | |
97 | | |
98 | def to_a | |
99 | @keys.map { |key| [ key, self[key] ] } | |
100 | end | |
101 | | |
102 | def each_key | |
103 | @keys.each { |key| yield key } | |
104 | end | |
105 | | |
106 | def each_value | |
107 | @keys.each { |key| yield self[key]} | |
108 | end | |
109 | | |
110 | def each | |
111 | @keys.each {|key| yield [key, self[key]]} | |
112 | end | |
113 | | |
114 | alias_method :each_pair, :each | |
115 | | |
116 | def clear | |
117 | super | |
118 | @keys.clear | |
119 | self | |
120 | end | |
121 | | |
122 | def shift | |
123 | k = @keys.first | |
124 | v = delete(k) | |
125 | [k, v] | |
126 | end | |
127 | | |
128 | def merge!(other_hash) | |
129 | if block_given? | |
130 | other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } | |
131 | else | |
132 | other_hash.each { |k, v| self[k] = v } | |
133 | end | |
134 | self | |
135 | end | |
136 | | |
137 | alias_method :update, :merge! | |
138 | | |
139 | def merge(other_hash, &block) | |
140 | dup.merge!(other_hash, &block) | |
141 | end | |
142 | | |
143 | # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. | |
144 | def replace(other) | |
145 | super | |
146 | @keys = other.keys | |
147 | self | |
148 | end | |
149 | | |
150 | def invert | |
151 | OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] | |
152 | end | |
153 | | |
154 | def inspect | |
155 | "#<OrderedHash #{super}>" | |
156 | end | |
157 | | |
158 | private | |
159 | def sync_keys! | |
160 | @keys.delete_if {|k| !has_key?(k)} | |
161 | end | |
162 | end | |
163 | end | |
164 | end | |
165 | end | |
166 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-core' | |
2 | | |
3 | module PayPal | |
4 | module SDK | |
5 | module REST | |
6 | autoload :VERSION, "paypal-sdk/rest/version" | |
7 | autoload :DataTypes, "paypal-sdk/rest/data_types" | |
8 | autoload :API, "paypal-sdk/rest/api" | |
9 | autoload :RequestDataType, "paypal-sdk/rest/request_data_type" | |
10 | autoload :SetAPI, "paypal-sdk/rest/set_api" | |
11 | autoload :GetAPI, "paypal-sdk/rest/get_api" | |
12 | autoload :ErrorHash, "paypal-sdk/rest/error_hash" | |
13 | | |
14 | include DataTypes | |
15 | include Core::Exceptions | |
16 | | |
17 | module ClassMethods | |
18 | def method_missing(name, *args) | |
19 | RequestDataType.send(name, *args) | |
20 | end | |
21 | end | |
22 | | |
23 | class << self | |
24 | def new(*args) | |
25 | API.new(*args) | |
26 | end | |
27 | | |
28 | include ClassMethods | |
29 | | |
30 | def included(klass) | |
31 | if klass.is_a? Module | |
32 | klass.extend(ClassMethods) | |
33 | end | |
34 | end | |
35 | end | |
36 | | |
37 | end | |
38 | end | |
39 | end | |
40 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/api.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-core' | |
2 | require 'paypal-sdk/rest/version' | |
3 | | |
4 | module PayPal | |
5 | module SDK | |
6 | module REST | |
7 | class API < Core::API::REST | |
8 | # include Services | |
9 | | |
10 | def initialize(environment = nil, options = {}) | |
11 | super("", environment, options) | |
12 | end | |
13 | | |
14 | class << self | |
15 | def user_agent | |
16 | @user_agent ||= "PayPalSDK/PayPal-Ruby-SDK #{PayPal::SDK::REST::VERSION} (#{sdk_library_details})" | |
17 | end | |
18 | end | |
19 | end | |
20 | end | |
21 | end | |
22 | end | |
23 | | |
24 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/error_hash.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module REST | |
4 | class ErrorHash < Core::Util::OrderedHash | |
5 | def self.convert(hash) | |
6 | error_hash = new | |
7 | hash.each{|key, value| | |
8 | error_hash[key] = value | |
9 | } | |
10 | error_hash | |
11 | end | |
12 | | |
13 | def []=(key, value) | |
14 | value = | |
15 | if value.is_a? Hash | |
16 | ErrorHash.convert(value) | |
17 | elsif value.is_a? Array and value[0].is_a? Hash | |
18 | value.map{|array_value| ErrorHash.convert(array_value) } | |
19 | else | |
20 | value | |
21 | end | |
22 | super(key, value) | |
23 | end | |
24 | | |
25 | def [](name) | |
26 | super(name.to_s) || super(name.to_sym) | |
27 | end | |
28 | | |
29 | def method_missing(name, *args) | |
30 | if keys.include?(name) or keys.include?(name.to_s) | |
31 | self[name] | |
32 | else | |
33 | super | |
34 | end | |
35 | end | |
36 | end | |
37 | end | |
38 | end | |
39 | end | |
40 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/get_api.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module REST | |
4 | module GetAPI | |
5 | # Get API object | |
6 | # === Example | |
7 | # Payment.api | |
8 | # payment.api | |
9 | def api | |
10 | @api || parent_api | |
11 | end | |
12 | | |
13 | # Parent API object | |
14 | def parent_api | |
15 | superclass.respond_to?(:api) ? superclass.api : RequestDataType.api | |
16 | end | |
17 | end | |
18 | end | |
19 | end | |
20 | end | |
21 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/request_data_type.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module REST | |
4 | module RequestDataType | |
5 | # Get a local API object or Class level API object | |
6 | def api | |
7 | @api || self.class.api | |
8 | end | |
9 | | |
10 | # Convert Hash object to ErrorHash object | |
11 | def error=(hash) | |
12 | @error = | |
13 | if hash.is_a? Hash | |
14 | ErrorHash.convert(hash) | |
15 | else | |
16 | hash | |
17 | end | |
18 | end | |
19 | | |
20 | class << self | |
21 | # Global API object | |
22 | # === Example | |
23 | # RequestDataType.api | |
24 | def api | |
25 | @api ||= API.new | |
26 | end | |
27 | | |
28 | # Setter for RequestDataType.api | |
29 | # === Example | |
30 | # RequestDataType.set_config(..) | |
31 | include SetAPI | |
32 | | |
33 | # Configure depended module, when RequestDataType is include. | |
34 | # === Example | |
35 | # class Payment < DataTypes | |
36 | # include RequestDataType | |
37 | # end | |
38 | # Payment.set_config(..) | |
39 | # payment.set_config(..) | |
40 | # Payment.api | |
41 | # payment.api | |
42 | def included(klass) | |
43 | klass.class_eval do | |
44 | extend GetAPI | |
45 | extend SetAPI | |
46 | include SetAPI | |
47 | end | |
48 | end | |
49 | end | |
50 | end | |
51 | end | |
52 | end | |
53 | end | |
54 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/set_api.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module REST | |
4 | module SetAPI | |
5 | # Set new api | |
6 | # === Examples | |
7 | # payment.set_config(:development) | |
8 | # payment.set_config(:client_id => "XYZ", :client_secret => "SECRET") | |
9 | # payment.set_config | |
10 | # payment.api = API.new(:development) | |
11 | def set_config(*args) | |
12 | if args[0].is_a?(API) | |
13 | @api = args[0] | |
14 | else | |
15 | @api ||= API.new({}) | |
16 | @api.set_config(*args) # Just override the configuration and Not | |
17 | @api | |
18 | end | |
19 | end | |
20 | alias_method :config=, :set_config | |
21 | alias_method :set_api, :set_config | |
22 | alias_method :api=, :set_config | |
23 | | |
24 | # Override token | |
25 | def token=(token) | |
26 | set_config( :client_id => nil, :client_secret => nil ).token = token | |
27 | end | |
28 | alias_method :"auth=", :"token=" | |
29 | | |
30 | # Override client id | |
31 | def client_id=(client_id) | |
32 | set_config(:client_id => client_id).token = nil | |
33 | end | |
34 | | |
35 | # Override client secret | |
36 | def client_secret=(client_secret) | |
37 | set_config(:client_secret => client_secret).token = nil | |
38 | end | |
39 | end | |
40 | end | |
41 | end | |
42 | end | |
43 | | |
-------------------------------------------------------------------------------- | |
/lib/paypal-sdk/rest/version.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal | |
2 | module SDK | |
3 | module REST | |
4 | VERSION = "1.7.4" | |
5 | end | |
6 | end | |
7 | end | |
8 | | |
-------------------------------------------------------------------------------- | |
/paypal-sdk-rest.gemspec: | |
-------------------------------------------------------------------------------- | |
1 | # -*- encoding: utf-8 -*- | |
2 | lib = File.expand_path('../lib', __FILE__) | |
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) | |
4 | require 'paypal-sdk/rest/version' | |
5 | | |
6 | Gem::Specification.new do |gem| | |
7 | gem.name = "paypal-sdk-rest" | |
8 | gem.version = PayPal::SDK::REST::VERSION | |
9 | gem.authors = ["PayPal"] | |
10 | gem.email = ["[email protected]"] | |
11 | gem.summary = %q{The PayPal REST SDK provides Ruby APIs to create, process and manage payment.} | |
12 | gem.description = %q{The PayPal REST SDK provides Ruby APIs to create, process and manage payment.} | |
13 | gem.homepage = "https://developer.paypal.com" | |
14 | | |
15 | gem.license = "PayPal SDK License" | |
16 | | |
17 | gem.files = Dir["{bin,spec,lib}/**/*"] + ["Rakefile", "README.md", "Gemfile"] + Dir["data/*"] | |
18 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } | |
19 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) | |
20 | gem.require_paths = ["lib"] | |
21 | | |
22 | gem.add_development_dependency('coveralls') | |
23 | gem.add_dependency('xml-simple') | |
24 | gem.add_dependency('multi_json', '~> 1.0') | |
25 | end | |
26 | | |
-------------------------------------------------------------------------------- | |
/samples/Gemfile: | |
-------------------------------------------------------------------------------- | |
1 | | |
2 | source "https://rubygems.org" | |
3 | | |
4 | if File.exists? "../paypal-sdk-rest.gemspec" | |
5 | # gem 'paypal-sdk-core', :git => "https://github.com/paypal/sdk-core-ruby.git" | |
6 | gem 'paypal-sdk-rest', :path => ".." | |
7 | else | |
8 | gem 'paypal-sdk-rest' | |
9 | end | |
10 | | |
11 | gem 'multi_json' | |
12 | gem 'coderay' | |
13 | gem 'sinatra' | |
14 | gem 'haml' | |
15 | | |
-------------------------------------------------------------------------------- | |
/samples/README.md: | |
-------------------------------------------------------------------------------- | |
1 | | |
2 | ## Run Sample App | |
3 | | |
4 | bundle install | |
5 | bundle exec rackup -p 3000 | |
6 | | |
7 | ## Run Sample in console | |
8 | | |
9 | bundle exec ruby payment/create_with_credit_card.rb | |
10 | | |
11 | ## Samples directory | |
12 | | |
13 | 1. payment/ | |
14 | 2. sale/ | |
15 | 3. credit_card/ | |
16 | | |
17 | ## Configuration | |
18 | | |
19 | Configuration in `config/paypal.yml` | |
20 | | |
21 | ```yaml | |
22 | test: &default | |
23 | mode: sandbox | |
24 | client_id: EBWKjlELKMYqRNQ6sYvFo64FtaRLRR5BdHEESmha49TM | |
25 | client_secret: EO422dn3gQLgDbuwqTjzrFgFtaRLRR5BdHEESmha49TM | |
26 | | |
27 | development: | |
28 | <<: *default | |
29 | | |
30 | production: | |
31 | mode: live | |
32 | client_id: CLIENT_ID | |
33 | client_secret: CLIENT_SECRET | |
34 | ``` | |
35 | | |
36 | | |
37 | | |
-------------------------------------------------------------------------------- | |
/samples/authorization/capture.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Capture Sample | |
2 | # Sample showing how to capture an authorized payment. | |
3 | # API used: POST /v1/payments/authorization/{authorization_id}/capture | |
4 | require 'paypal-sdk-rest' | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | begin | |
8 | @authorization = Authorization.find("84H59271K1950474X") | |
9 | @capture = @authorization.capture({ | |
10 | :amount => { | |
11 | :currency => "USD", | |
12 | :total => "1.00" }, | |
13 | :is_final_capture => true }) | |
14 | | |
15 | if @capture.success? # Return true or false | |
16 | logger.info "Capture[#{@capture.id}]" | |
17 | else | |
18 | logger.error @capture.error.inspect | |
19 | end | |
20 | rescue ResourceNotFound => err | |
21 | # It will throw ResourceNotFound exception if the payment not found | |
22 | logger.error "Authorization Not Found" | |
23 | end | |
24 | | |
25 | | |
-------------------------------------------------------------------------------- | |
/samples/authorization/find.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #GetAuthorization Sample | |
2 | # Sample showing how to retrieve authorized payment details. | |
3 | # API used: GET /v1/payments/authorization/{authorization_id} | |
4 | require 'paypal-sdk-rest' | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | begin | |
9 | @authorization = Authorization.find("7WU36907W63353628") | |
10 | logger.info "Got Authorization[#{@authorization.id}]" | |
11 | rescue ResourceNotFound => err | |
12 | logger.error "Authorization Not Found" | |
13 | end | |
14 | | |
-------------------------------------------------------------------------------- | |
/samples/authorization/reauthorize.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Reauthorization Sample | |
2 | # Sample showing how to do a reauthorization. | |
3 | # API used: POST /v1/payments/authorization/{authorization_id}/reauthorize | |
4 | require 'paypal-sdk-rest' | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | begin | |
9 | # ###Reauthorization | |
10 | # Retrieve a authorization id from authorization object | |
11 | # by making a Payment with payment type as `PayPal` with intent | |
12 | # as `authorize`. You can reauthorize a payment only once 4 to 29 | |
13 | # days after 3-day honor period for the original authorization | |
14 | # expires. | |
15 | @authorization = Authorization.find("7GH53639GA425732B") | |
16 | | |
17 | @authorization.amount = { | |
18 | :currency => "USD", | |
19 | :total => "7.00" }; | |
20 | | |
21 | if @authorization.reauthorize # Return true or false | |
22 | logger.info "Reauthorization[#{@authorization.id}]" | |
23 | else | |
24 | logger.error @authorization.error.inspect | |
25 | end | |
26 | rescue ResourceNotFound => err | |
27 | # It will throw ResourceNotFound exception if the payment not found | |
28 | logger.error "Authorization Not Found" | |
29 | end | |
30 | | |
-------------------------------------------------------------------------------- | |
/samples/authorization/void.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Void Sample | |
2 | # Sample showing how to void an authorized payment. | |
3 | # API used: POST /v1/payments/authorization/{authorization_id}/void | |
4 | require 'paypal-sdk-rest' | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | begin | |
8 | @authorization = Authorization.find("1SU1944563257711X") | |
9 | | |
10 | if @authorization.void # Return true or false | |
11 | logger.info "Void an Authorization[#{@authorization.id}]" | |
12 | else | |
13 | logger.error @authorization.error.inspect | |
14 | end | |
15 | rescue ResourceNotFound => err | |
16 | # It will throw ResourceNotFound exception if the payment not found | |
17 | logger.error "Authorization Not Found" | |
18 | end | |
19 | | |
-------------------------------------------------------------------------------- | |
/samples/capture/find.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #GetCapture Sample | |
2 | # Sample showing how to retrieve captured payment details. | |
3 | # API used: GET /v1/payments/capture/{capture_id} | |
4 | require 'paypal-sdk-rest' | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | begin | |
9 | @capture = Capture.find("7YR91301C22810733") | |
10 | logger.info "Got Capture[#{@sale.id}]" | |
11 | rescue ResourceNotFound => err | |
12 | logger.error "Capture Not Found" | |
13 | end | |
14 | | |
-------------------------------------------------------------------------------- | |
/samples/capture/refund.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #RefundCapture Sample | |
2 | # Sample showing how to refund captured payment. | |
3 | # API used: GET /v1/payments/capture/{capture_id}/refund | |
4 | require 'paypal-sdk-rest' | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | begin | |
9 | @capture = Capture.find("9YB06173L6274542A") | |
10 | @refund = @capture.refund_request({ | |
11 | :amount => { | |
12 | :currency => "USD", | |
13 | :total => "0.01" } | |
14 | }) | |
15 | | |
16 | # Check refund status | |
17 | if @refund.success? | |
18 | logger.info "Refund[#{@refund.id}] Success" | |
19 | else | |
20 | logger.error "Unable to Refund" | |
21 | logger.error @refund.error.inspect | |
22 | end | |
23 | rescue ResourceNotFound => err | |
24 | # It will throw ResourceNotFound exception if the payment not found | |
25 | logger.error "Authorization Not Found" | |
26 | end | |
27 | | |
-------------------------------------------------------------------------------- | |
/samples/config.ru: | |
-------------------------------------------------------------------------------- | |
1 | require './app' | |
2 | | |
3 | run App | |
4 | | |
-------------------------------------------------------------------------------- | |
/samples/config/paypal.yml: | |
-------------------------------------------------------------------------------- | |
1 | test: &default | |
2 | # change mode to "security-test-sandbox" to test TLSv1.2 connection | |
3 | mode: sandbox | |
4 | client_id: AYSq3RDGsmBLJE-otTkBtM-jBRd1TCQwFf9RGfwddNXWz0uFU9ztymylOhRS | |
5 | client_secret: EGnHDxD_qRPdaLdZz8iCr8N7_MzF-YHPTkjs6NKYQvQSBngp4PTTVWkPZRbL | |
6 | #Your OpenID redirect URI must be the same value as your app's return URL. It is configured in the PayPal Developer Dashboard ( https://developer.paypal.com/developer/applications/ ). | |
7 | openid_redirect_uri: http://localhost/paypal/PayPal-PHP-SDK/sample/lipp/UserConsentRedirect.php?success=true | |
8 | | |
9 | development: | |
10 | <<: *default | |
11 | | |
12 | production: | |
13 | mode: live | |
14 | client_id: CLIENT_ID | |
15 | client_secret: CLIENT_SECRET | |
16 | # rest_endpoint: "https://api.paypal.com" | |
17 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/cancel.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice | |
7 | @invoice = RunSample.run('invoice/send_invoice.rb', '@invoice') | |
8 | | |
9 | options = { | |
10 | "subject" => "Past due", | |
11 | "note" => "Canceling invoice", | |
12 | "send_to_merchant" => true, | |
13 | "send_to_payer" => true | |
14 | } | |
15 | | |
16 | if @invoice.cancel(options) | |
17 | logger.info "Invoice[#{@invoice.id}] cancel successfully" | |
18 | else | |
19 | logger.error @invoice.error.inspect | |
20 | end | |
21 | | |
22 | rescue ResourceNotFound => err | |
23 | logger.error "Invoice Not Found" | |
24 | end | |
25 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/create.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | @invoice = Invoice.new({ | |
6 | "merchant_info" => { | |
7 | "email" => "[email protected]", | |
8 | "first_name" => "Dennis", | |
9 | "last_name" => "Doctor", | |
10 | "business_name" => "Medical Professionals, LLC", | |
11 | "phone" => { | |
12 | "country_code" => "001", | |
13 | "national_number" => "5032141716" | |
14 | }, | |
15 | "address" => { | |
16 | "line1" => "1234 Main St.", | |
17 | "city" => "Portland", | |
18 | "state" => "OR", | |
19 | "postal_code" => "97217", | |
20 | "country_code" => "US" | |
21 | } | |
22 | }, | |
23 | "billing_info" => [ { "email" => "[email protected]" } ], | |
24 | "items" => [ | |
25 | { | |
26 | "name" => "Sutures", | |
27 | "quantity" => 100, | |
28 | "unit_price" => { | |
29 | "currency" => "USD", | |
30 | "value" => 5 | |
31 | } | |
32 | } | |
33 | ], | |
34 | "note" => "Medical Invoice 16 Jul, 2013 PST", | |
35 | "payment_term" => { | |
36 | "term_type" => "NET_45" | |
37 | }, | |
38 | "shipping_info" => { | |
39 | "first_name" => "Sally", | |
40 | "last_name" => "Patient", | |
41 | "business_name" => "Not applicable", | |
42 | "phone" => { | |
43 | "country_code" => "001", | |
44 | "national_number" => "5039871234" | |
45 | }, | |
46 | "address" => { | |
47 | "line1" => "1234 Broad St.", | |
48 | "city" => "Portland", | |
49 | "state" => "OR", | |
50 | "postal_code" => "97216", | |
51 | "country_code" => "US" | |
52 | } | |
53 | } | |
54 | }) | |
55 | | |
56 | if @invoice.create | |
57 | logger.info "Invoice[#{@invoice.id}] created successfully" | |
58 | else | |
59 | logger.error @invoice.error.inspect | |
60 | end | |
61 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/get.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice | |
7 | @invoice = RunSample.run('invoice/create.rb', '@invoice') | |
8 | | |
9 | @invoice= Invoice.find(@invoice.id) | |
10 | logger.info "Got Invoice Details for Invoice[#{@invoice.id}]" | |
11 | | |
12 | rescue ResourceNotFound => err | |
13 | logger.error "Invoice Not Found" | |
14 | end | |
15 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/get_all.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | @invoices = Invoice.get_all( :total_count_required => true ) | |
6 | logger.info "Invoices: #{@invoices.total_count}" | |
7 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/remind.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | | |
6 | begin | |
7 | # Create an invoice | |
8 | @invoice = RunSample.run('invoice/send_invoice.rb', '@invoice') | |
9 | | |
10 | options = { | |
11 | "subject" => "Past due", | |
12 | "note" => "Please pay soon", | |
13 | "send_to_merchant" => true | |
14 | } | |
15 | | |
16 | if @invoice.remind(options) | |
17 | logger.info "Invoice[#{@invoice.id}] remind successfully" | |
18 | else | |
19 | logger.error @invoice.error.inspect | |
20 | end | |
21 | | |
22 | rescue ResourceNotFound => err | |
23 | logger.error "Invoice Not Found" | |
24 | end | |
25 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/send_invoice.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice | |
7 | @invoice = RunSample.run('invoice/create.rb', '@invoice') | |
8 | | |
9 | if @invoice.send_invoice | |
10 | logger.info "Invoice[#{@invoice.id}] send successfully" | |
11 | else | |
12 | logger.error @invoice.error.inspect | |
13 | end | |
14 | rescue ResourceNotFound => err | |
15 | logger.error "Invoice Not Found" | |
16 | end | |
17 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice/third_party_invoice.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | # Using Log In with PayPal, third party merchants can authorize your application to create and submit invoices on their behalf. | |
6 | # See https://developer.paypal.com/docs/integration/direct/identity/log-in-with-paypal/ for more details about Log In with PayPal. | |
7 | | |
8 | | |
9 | # Step 1. Generate a Log In with PayPal authorization URL. The third party merchant will need to visit this URL and authorize your request. You should use the `openid`, `https://uri.paypal.com/services/invoicing`, and `email` scopes at the minimum. | |
10 | #logger.info(PayPal::SDK::OpenIDConnect.authorize_url({'scope': 'openid https://uri.paypal.com/services/invoicing email'})) | |
11 | | |
12 | # For example, the URL to redirect the third party merchant may be like: | |
13 | # https://www.sandbox.paypal.com/signin/authorize?client_id=AYSq3RDGsmBLJE-otTkBtM-jBRd1TCQwFf9RGfwddNXWz0uFU9ztymylOhRS&scope=openid%20https%3A%2F%2Furi.paypal.com%2Fservices%2Finvoicing%20email&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2Fpaypal%2FPayPal-PHP-SDK%2Fsample%2Flipp%2FUserConsentRedirect.php%3Fsuccess%3Dtrue | |
14 | | |
15 | # Step 2. After the third party merchant authorizes your request, Log In with PayPal will redirect the browser to your configured openid_redirect_uri with an authorization code query parameter value. | |
16 | | |
17 | # For example, after the merchant successfully authorizes your request, they will be redirected to the merchant's website configured via `openid_redirect_uri` like: | |
18 | # http://localhost/paypal/PayPal-PHP-SDK/sample/lipp/UserConsentRedirect.php?success=true&scope=https%3A%2F%2Furi.paypal.com%2Fservices%2Finvoicing+openid+email&code=q6DV3YTMUDquwMQ88I7VyE_Ou1ksKcKVo1b_a8zqUQ7fGCxLoLajHIBROQ5yxcM0dNWVWPQaovH35dJmdmunNYrNKao8UlpY5LVMuTgnxZ6ce6UL3XuEd3JXuuPtdg-4feXyMA25oh2aM5o5HTtSeSG1Ag596q9tk90dfJQ8gxhFTw1bV | |
19 | | |
20 | # Step 3. Your web app should parse the query parameters for the authorization `code` value. You should use the `code` value to get a refresh token and securely save it for later use. | |
21 | =begin | |
22 | authorization_code = "q6DV3YTMUDquwMQ88I7VyE_Ou1ksKcKVo1b_a8zqUQ7fGCxLoLajHIBROQ5yxcM0dNWVWPQaovH35dJmdmunNYrNKao8UlpY5LVMuTgnxZ6ce6UL3XuEd3JXuuPtdg-4feXyMA25oh2aM5o5HTtSeSG1Ag596q9tk90dfJQ8gxhFTw1bV" | |
23 | tokeninfo = PayPal::SDK::OpenIDConnect::Tokeninfo.create(authorization_code) | |
24 | logger.info(tokeninfo); | |
25 | # Response 200 | |
26 | tokeninfo = { | |
27 | token_type: 'Bearer', | |
28 | expires_in: '28800', | |
29 | refresh_token: 'J5yFACP3Y5dqdWCdN3o9lNYz0XyR01IHNMQn-E4r6Ss38rqbQ1C4rC6PSBhJvB_tte4WZsWe8ealMl-U_GMSz30dIkKaovgN41Xf8Sz0EGU55da6tST5I6sg3Rw', | |
30 | id_token: '<some value>', | |
31 | access_token: '<some value>' | |
32 | } | |
33 | =end | |
34 | | |
35 | # Step 4. You can use the refresh token to get the third party merchant's email address to use in the invoice, and then you can create an invoice on behalf of the third party merchant. | |
36 | | |
37 | refresh_token = "J5yFACP3Y5dqdWCdN3o9lNYz0XyR01IHNMQn-E4r6Ss38rqbQ1C4rC6PSBhJvB_tte4WZsWe8ealMl-U_GMSz30dIkKaovgN41Xf8Sz0EGU55da6tST5I6sg3Rw" | |
38 | | |
39 | tokeninfo = PayPal::SDK::OpenIDConnect::DataTypes::Tokeninfo.create_from_refresh_token(refresh_token) | |
40 | access_token = tokeninfo.access_token | |
41 | userinfo = PayPal::SDK::OpenIDConnect::Userinfo.get(tokeninfo.access_token) | |
42 | | |
43 | # more attribute available in tokeninfo | |
44 | logger.info tokeninfo.to_hash | |
45 | logger.info userinfo.email | |
46 | | |
47 | @invoice = Invoice.new({ | |
48 | "merchant_info" => { | |
49 | "email" => userinfo.email, | |
50 | "first_name" => "Dennis", | |
51 | "last_name" => "Doctor", | |
52 | "business_name" => "Medical Professionals, LLC", | |
53 | "phone" => { | |
54 | "country_code" => "001", | |
55 | "national_number" => "5032141716" | |
56 | }, | |
57 | "address" => { | |
58 | "line1" => "1234 Main St.", | |
59 | "city" => "Portland", | |
60 | "state" => "OR", | |
61 | "postal_code" => "97217", | |
62 | "country_code" => "US" | |
63 | } | |
64 | }, | |
65 | "billing_info" => [ { "email" => "[email protected]" } ], | |
66 | "items" => [ | |
67 | { | |
68 | "name" => "Sutures", | |
69 | "quantity" => 100, | |
70 | "unit_price" => { | |
71 | "currency" => "USD", | |
72 | "value" => 5 | |
73 | } | |
74 | } | |
75 | ], | |
76 | "note" => "Medical Invoice 16 Jul, 2013 PST", | |
77 | "payment_term" => { | |
78 | "term_type" => "NET_45" | |
79 | }, | |
80 | "shipping_info" => { | |
81 | "first_name" => "Sally", | |
82 | "last_name" => "Patient", | |
83 | "business_name" => "Not applicable", | |
84 | "phone" => { | |
85 | "country_code" => "001", | |
86 | "national_number" => "5039871234" | |
87 | }, | |
88 | "address" => { | |
89 | "line1" => "1234 Broad St.", | |
90 | "city" => "Portland", | |
91 | "state" => "OR", | |
92 | "postal_code" => "97216", | |
93 | "country_code" => "US" | |
94 | } | |
95 | } | |
96 | }.merge( :token => access_token ) | |
97 | ) | |
98 | | |
99 | # create and send invoice | |
100 | if @invoice.create | |
101 | logger.info "Invoice[#{@invoice.id}] created successfully" | |
102 | if @invoice.send_invoice == true | |
103 | logger.info "Invoice[#{@invoice.id}] sent successfully" | |
104 | # To find invoice please use access token generated from refresh token | |
105 | if @invoice= Invoice.find("#{@invoice.id}", access_token) | |
106 | logger.info "Got Invoice Details for Invoice[#{@invoice.id}]" | |
107 | else | |
108 | logger.error @invoice.error.inspect | |
109 | end | |
110 | else | |
111 | logger.error @invoice.error.inspect | |
112 | end | |
113 | else | |
114 | logger.error @invoice.error.inspect | |
115 | end | |
116 | | |
117 | | |
118 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice_template/create.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | require 'securerandom' | |
4 | include PayPal::SDK::Core::Logging | |
5 | | |
6 | @template = Template.new( | |
7 | { | |
8 | "name" => SecureRandom.uuid, | |
9 | "default"=> true, | |
10 | "unit_of_measure"=> "HOURS", | |
11 | "template_data"=> { | |
12 | "tax_calculated_after_discount"=> false, | |
13 | "tax_inclusive"=> false, | |
14 | "note"=> "Thank you for your business", | |
15 | "logo_url"=> "https://pics.paypal.com/v1/images/redDot.jpeg", | |
16 | "items"=> [ | |
17 | { | |
18 | "name"=> "Nutri Bullet", | |
19 | "quantity"=> "1", | |
20 | "unit_price"=> { | |
21 | "currency"=> "USD", | |
22 | "value"=> "50.00" | |
23 | } | |
24 | } | |
25 | ], | |
26 | "merchant_info"=> { | |
27 | "email"=> "[email protected]" | |
28 | } | |
29 | }, | |
30 | "settings"=> [ | |
31 | { | |
32 | "field_name"=> "custom", | |
33 | "display_preference"=> { | |
34 | "hidden"=> true | |
35 | } | |
36 | }, | |
37 | { | |
38 | "field_name"=> "items.date", | |
39 | "display_preference"=> { | |
40 | "hidden"=> true | |
41 | } | |
42 | } | |
43 | ] | |
44 | }) | |
45 | | |
46 | if @template.create | |
47 | logger.info "Tempalte[#{@template.template_id}] created successfully" | |
48 | else | |
49 | logger.error @template.error.inspect | |
50 | end | |
51 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice_template/delete.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice template | |
7 | @template = RunSample.run('invoice_template/create.rb', '@template') | |
8 | | |
9 | if @template.delete | |
10 | logger.info "Deleted invoice template : [#{@template.template_id}]" | |
11 | else | |
12 | logger.info "Deleting failed" | |
13 | end | |
14 | rescue ResourceNotFound => err | |
15 | logger.error "Template Not Found" | |
16 | end | |
17 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice_template/get.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice template | |
7 | @template = RunSample.run('invoice_template/create.rb', '@template') | |
8 | | |
9 | @template= Template.get(@template.template_id) | |
10 | logger.info "Got Template Details for [#{@template.template_id}]" | |
11 | | |
12 | rescue ResourceNotFound => err | |
13 | logger.error "Template Not Found" | |
14 | end | |
15 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice_template/get_all.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | @templates = Templates.get_all( :fields => "all" ) | |
6 | logger.info "Fetched all templates" | |
7 | | |
-------------------------------------------------------------------------------- | |
/samples/invoice_template/update.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | include PayPal::SDK::REST | |
3 | include PayPal::SDK::Core::Logging | |
4 | | |
5 | begin | |
6 | # Create an invoice template | |
7 | @template = RunSample.run('invoice_template/create.rb', '@template') | |
8 | | |
9 | @template.custom = nil | |
10 | @template.template_data.note = "Something else" | |
11 | | |
12 | @template.update | |
13 | rescue ResourceNotFound => err | |
14 | logger.error "Template Not Found" | |
15 | end | |
16 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/create.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | require "securerandom" | |
4 | | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | @webhook = Webhook.new({ | |
9 | :url => "https://www.yeowza.com/paypal_webhook_"+SecureRandom.hex(8), | |
10 | :event_types => [ | |
11 | { | |
12 | :name => "PAYMENT.AUTHORIZATION.CREATED" | |
13 | }, | |
14 | { | |
15 | :name => "PAYMENT.AUTHORIZATION.VOIDED" | |
16 | } | |
17 | ] | |
18 | }) | |
19 | | |
20 | begin | |
21 | @webhook = @webhook.create | |
22 | logger.info "Webhook[#{@webhook.id}] created successfully" | |
23 | rescue ResourceNotFound => err | |
24 | logger.error @webhook.error.inspect | |
25 | end | |
26 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/delete_webhook.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | @webhook = RunSample.run('notifications/get_webhook.rb', '@webhook') | |
10 | | |
11 | if @webhook.delete | |
12 | logger.info "Webhook[#{@webhook.id}] deleted successfully" | |
13 | else | |
14 | logger.error "Unable to delete Webhook[#{@webhook.id}]" | |
15 | logger.error @webhook.error.inspect | |
16 | end | |
17 | rescue ResourceNotFound => err | |
18 | logger.error "Webhook Not Found" | |
19 | end | |
20 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/get_all_webhooks.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | RunSample.run('notifications/get_webhook.rb', '@webhook') | |
10 | RunSample.run('notifications/get_webhook.rb', '@webhook') | |
11 | | |
12 | # Get webhooks | |
13 | @webhooks_list = Webhook.all() | |
14 | | |
15 | logger.info "List Webhooks:" | |
16 | @webhooks_list.webhooks.each do |webhook| | |
17 | logger.info " -> Webhook Event Name[#{webhook.id}]" | |
18 | end | |
19 | | |
20 | rescue ResourceNotFound => err | |
21 | logger.error "Webhooks not found" | |
22 | ensure | |
23 | # Clean up webhooks as not to get into a bad state | |
24 | @webhooks_list.webhooks.each do |webhook| | |
25 | webhook.delete | |
26 | end | |
27 | end | |
28 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/get_subscribed_webhooks_event_types.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | @webhook = RunSample.run('notifications/create.rb', '@webhook') | |
10 | | |
11 | # Get Payout Batch Status | |
12 | @webhook_event_types = Webhook.get_event_types(@webhook.id) | |
13 | logger.info "List Webhook Subscribed Events:" | |
14 | @webhook_event_types.event_types.each do |event| | |
15 | logger.info " -> Webhook Event Name[#{event.name}]" | |
16 | end | |
17 | | |
18 | rescue ResourceNotFound => err | |
19 | logger.error "Webhook Events not found" | |
20 | end | |
21 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/get_webhook.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | @webhook = RunSample.run('notifications/create.rb', '@webhook') | |
10 | | |
11 | @webhook = Webhook.get(@webhook.id) | |
12 | logger.info "Got Webhook[#{@webhook.id}]" | |
13 | | |
14 | rescue ResourceNotFound => err | |
15 | logger.error "Payout Batch not Found" | |
16 | end | |
17 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/get_webhook_event.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | @webhook_event = WebhookEvent.get("WH-7Y7254563A4550640-11V2185806837105M") | |
9 | @resource = @webhook_event.get_resource() | |
10 | | |
11 | rescue ResourceNotFound => err | |
12 | logger.error @webhook.error.inspect | |
13 | end | |
14 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/get_webhooks_event_types.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | # Get webhooks event types | |
10 | @webhooks_event_types = WebhooksEventType.all() | |
11 | | |
12 | logger.info "List Webhook Events:" | |
13 | @webhooks_event_types.event_types.each do |event| | |
14 | logger.info " -> Webhook Event Name[#{event.name}]" | |
15 | end | |
16 | | |
17 | rescue ResourceNotFound => err | |
18 | logger.error "Webhook Events not found" | |
19 | end | |
20 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/resend_webhook_event.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | @webhook_event = WebhookEvent.resend("WH-7Y7254563A4550640-11V2185806837105M") | |
9 | logger.info "Resent Webhook Event[#{@webhook_event.id}]" | |
10 | | |
11 | rescue ResourceNotFound => err | |
12 | logger.error @webhook.error.inspect | |
13 | end | |
14 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/search_webhook_event.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | # Get webhooks event types | |
10 | @webhooks_events = WebhookEvent.search(10, "2013-03-06T11:00:00Z", "2013-04-06T11:00:00Z") | |
11 | | |
12 | logger.info "List Webhook Events:" | |
13 | @webhooks_events.events.each do |event| | |
14 | logger.info " -> Webhook Event Name[#{event.name}]" | |
15 | end | |
16 | | |
17 | rescue ResourceNotFound => err | |
18 | logger.error "Webhook Events not found" | |
19 | end | |
20 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/simulate_event.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | @webhook_event = Webhook.simulate_event(nil, "https://requestb.in/1jbk3uv1", "PAYMENT.CAPTURE.COMPLETED") | |
10 | | |
11 | @resource = @webhook_event.get_resource() | |
12 | | |
13 | rescue ResourceNotFound => err | |
14 | logger.error "Payout Batch not Found" | |
15 | end | |
16 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/update_webhook.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | require "securerandom" | |
4 | | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | begin | |
9 | @webhook = RunSample.run('notifications/create.rb', '@webhook') | |
10 | | |
11 | # set up a patch request | |
12 | patch = Patch.new | |
13 | patch.op = "replace" | |
14 | patch.path = "/url" | |
15 | patch.value = "https://www.yeowza.com/paypal_webhook_new_url_"+SecureRandom.hex(8) | |
16 | | |
17 | if @webhook.update(patch) | |
18 | logger.info "Webhook[#{@webhook.id}] updated successfully" | |
19 | else | |
20 | logger.error @webhook.error.inspect | |
21 | end | |
22 | rescue ResourceNotFound => err | |
23 | logger.error @webhook.error.inspect | |
24 | end | |
25 | | |
-------------------------------------------------------------------------------- | |
/samples/notifications/verify_webhook_event.rb: | |
-------------------------------------------------------------------------------- | |
1 | # webhook validation (see https://developer.paypal.com/docs/integration/direct/rest-webhooks-overview/#event-signature ) | |
2 | | |
3 | require 'paypal-sdk-rest' | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | # Below code demonstrates how to parse information from the request coming from PayPal webhook servers. | |
8 | # For more information: https://github.com/paypal/PayPal-Ruby-SDK/issues/205#issuecomment-249051953 | |
9 | # webhook headers required for event verification | |
10 | | |
11 | # actual_signature = request.headers["Paypal-Transmission-Sig"] | |
12 | # auth_algo = request.headers["Paypal-Auth-Algo"] | |
13 | # auth_algo.sub!(/withRSA/i, "") | |
14 | # cert_url = request.headers["Paypal-Cert-Url"] | |
15 | # transmission_id = request.headers["Paypal-Transmission-Id"] | |
16 | # timestamp = request.headers["Paypal-Transmission-Time"] | |
17 | # webhook_id = ENV['PAYPAL_WEBHOOK_ID'] #The webhook_id provided by PayPal when webhook is created on the PayPal developer site | |
18 | # event_body = params["paypal"].to_json | |
19 | | |
20 | # MOCK DATA for Sample purpose only. Please use above code for real use cases. | |
21 | # Paypal-Transmission-Sig header | |
22 | actual_signature = "thy4/U002quzxFavHPwbfJGcc46E8rc5jzgyeafWm5mICTBdY/8rl7WJpn8JA0GKA+oDTPsSruqusw+XXg5RLAP7ip53Euh9Xu3UbUhQFX7UgwzE2FeYoY6lyRMiiiQLzy9BvHfIzNIVhPad4KnC339dr6y2l+mN8ALgI4GCdIh3/SoJO5wE64Bh/ueWtt8EVuvsvXfda2Le5a2TrOI9vLEzsm9GS79hAR/5oLexNz8UiZr045Mr5ObroH4w4oNfmkTaDk9Rj0G19uvISs5QzgmBpauKr7Nw++JI0pr/v5mFctQkoWJSGfBGzPRXawrvIIVHQ9Wer48GR2g9ZiApWg==" | |
23 | # Paypal-Auth-Algo header | |
24 | auth_algo = "sha256" | |
25 | # Paypal-Cert-Url header | |
26 | cert_url = "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77" | |
27 | # other required items for event verification | |
28 | transmission_id = "dfb3be50-fd74-11e4-8bf3-77339302725b" | |
29 | timestamp = "2015-05-18T15:45:13Z" | |
30 | webhook_id = "4JH86294D6297924G" | |
31 | event_body = '{"id":"WH-0G2756385H040842W-5Y612302CV158622M","create_time":"2015-05-18T15:45:13Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 20.0 USD","resource":{"id":"4EU7004268015634R","create_time":"2015-05-18T15:44:02Z","update_time":"2015-05-18T15:44:21Z","amount":{"total":"20.00","currency":"USD"},"payment_mode":"INSTANT_TRANSFER","state":"completed","protection_eligibility":"ELIGIBLE","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","parent_payment":"PAY-86C81811X5228590KKVNARQQ","transaction_fee":{"value":"0.88","currency":"USD"},"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-86C81811X5228590KKVNARQQ","rel":"parent_payment","method":"GET"}]},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M/resend","rel":"resend","method":"POST"}]}' | |
32 | | |
33 | # verify webhook | |
34 | # pass all the field to `WebhookEvent.verify()` method which returns true or false based on validation | |
35 | valid = WebhookEvent.verify(transmission_id, timestamp, webhook_id, event_body, cert_url, actual_signature, auth_algo) | |
36 | | |
37 | if valid | |
38 | logger.info "webhook event #{webhook_id} has been verified" | |
39 | else | |
40 | logger.info "webhook event #{webhook_id} validation failed" | |
41 | end | |
42 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/all.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #GetPaymentList Sample | |
2 | # This sample code demonstrate how you can | |
3 | # retrieve a list of all Payment resources | |
4 | # you've created using the Payments API. | |
5 | # Note various query parameters that you can | |
6 | # use to filter, and paginate through the | |
7 | # payments list. | |
8 | # API used: GET /v1/payments/payments | |
9 | require 'paypal-sdk-rest' | |
10 | include PayPal::SDK::REST | |
11 | include PayPal::SDK::Core::Logging | |
12 | | |
13 | # ###Retrieve | |
14 | # Retrieve the PaymentHistory by calling the | |
15 | # `all` method | |
16 | # on the Payment class | |
17 | # Refer the API documentation | |
18 | # for valid values for keys | |
19 | # Supported paramters are :count, :next_id | |
20 | @payment_history = Payment.all( :count => 5 ) | |
21 | | |
22 | # List Payments | |
23 | logger.info "List Payment:" | |
24 | @payment_history.payments.each do |payment| | |
25 | logger.info " -> Payment[#{payment.id}]" | |
26 | end | |
27 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/create_future_payment.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Future Payment Sample | |
2 | # This sample code demonstrate how you can | |
3 | # create a Future Payment. | |
4 | # This is a console application, so run it with `bundle exec ruby payment/create_future_payment.rb` on terminal | |
5 | # API used: CREATE /v1/payments/payments | |
6 | # /v1/oauth2/token | |
7 | | |
8 | require 'paypal-sdk-rest' | |
9 | include PayPal::SDK::REST::DataTypes | |
10 | include PayPal::SDK::Core::Logging | |
11 | | |
12 | | |
13 | # authorization code from mobile sdk | |
14 | authorization_code = '' | |
15 | | |
16 | # correlation ID from mobile sdk | |
17 | correlation_id = '' | |
18 | | |
19 | # Initialize the payment object | |
20 | payment = { | |
21 | "intent" => "authorize", | |
22 | "payer" => { | |
23 | "payment_method" => "paypal" }, | |
24 | "transactions" => [ { | |
25 | "amount" => { | |
26 | "total" => "1.00", | |
27 | "currency" => "USD" }, | |
28 | "description" => "This is the payment transaction description." } ] } | |
29 | | |
30 | # exchange authorization code with refresh/access token | |
31 | logger.info "Exchange authorization code with refresh/access token" | |
32 | tokeninfo = FuturePayment.exch_token(authorization_code) | |
33 | | |
34 | # get access_token, refresh_token from tokeninfo. refresh_token can be exchanged with access token. See https://github.com/paypal/PayPal-Ruby-SDK#openidconnect-samples | |
35 | access_token = tokeninfo.access_token | |
36 | logger.info "Successfully retrieved access_token=#{access_token} refresh_token=#{tokeninfo.refresh_token}" | |
37 | | |
38 | # more attribute available in tokeninfo | |
39 | logger.info tokeninfo.to_hash | |
40 | | |
41 | # Create Payments | |
42 | logger.info "Create Future Payment" | |
43 | future_payment = FuturePayment.new(payment.merge( :token => access_token )) | |
44 | success = future_payment.create(correlation_id) | |
45 | | |
46 | # check response for status | |
47 | if success | |
48 | logger.info "future payment successfully created" | |
49 | else | |
50 | logger.info "future payment creation failed" | |
51 | end | |
52 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/create_third_party_with_paypal.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Create Third Party Payment Using PayPal Sample | |
2 | # This sample code demonstrates how you can process a | |
3 | # PayPal Account based Payment. | |
4 | # API used: /v1/payments/payment | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | # ###Payment | |
10 | # A Payment Resource; create one using | |
11 | # the above types and intent as 'sale' | |
12 | @payment = Payment.new({ | |
13 | :intent => "sale", | |
14 | | |
15 | # ###Payer | |
16 | # A resource representing a Payer that funds a payment | |
17 | # Payment Method as 'paypal' | |
18 | :payer => { | |
19 | :payment_method => "paypal" }, | |
20 | | |
21 | # ###Redirect URLs | |
22 | :redirect_urls => { | |
23 | :return_url => "http://localhost:3000/payment/execute", | |
24 | :cancel_url => "http://localhost:3000/" }, | |
25 | | |
26 | # ###Transaction | |
27 | # A transaction defines the contract of a | |
28 | # payment - what is the payment for and who | |
29 | # is fulfilling it. | |
30 | :transactions => [{ | |
31 | | |
32 | # ### Payee | |
33 | # Specify a payee with that user's email or merchant id | |
34 | # Merchant Id can be found at https://www.paypal.com/businessprofile/settings/ | |
35 | :payee => { | |
36 | :email => "[email protected]" | |
37 | }, | |
38 | | |
39 | # Item List | |
40 | :item_list => { | |
41 | :items => [{ | |
42 | :name => "item", | |
43 | :sku => "item", | |
44 | :price => "5", | |
45 | :currency => "USD", | |
46 | :quantity => 1 }]}, | |
47 | | |
48 | # ###Amount | |
49 | # Let's you specify a payment amount. | |
50 | :amount => { | |
51 | :total => "5", | |
52 | :currency => "USD" }, | |
53 | :description => "This is the payment transaction description." }]}) | |
54 | | |
55 | # Create Payment and return status | |
56 | if @payment.create | |
57 | # Redirect the user to given approval url | |
58 | @redirect_url = @payment.links.find{|v| v.rel == "approval_url" }.href | |
59 | logger.info "Payment[#{@payment.id}]" | |
60 | logger.info "Redirect: #{@redirect_url}" | |
61 | else | |
62 | logger.error @payment.error.inspect | |
63 | end | |
64 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/create_with_paypal.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #Create Payment Using PayPal Sample | |
2 | # This sample code demonstrates how you can process a | |
3 | # PayPal Account based Payment. | |
4 | # API used: /v1/payments/payment | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | # ###Payment | |
10 | # A Payment Resource; create one using | |
11 | # the above types and intent as 'sale' | |
12 | @payment = Payment.new({ | |
13 | :intent => "sale", | |
14 | | |
15 | # ###Payer | |
16 | # A resource representing a Payer that funds a payment | |
17 | # Payment Method as 'paypal' | |
18 | :payer => { | |
19 | :payment_method => "paypal" }, | |
20 | | |
21 | # ###Redirect URLs | |
22 | :redirect_urls => { | |
23 | :return_url => "http://localhost:3000/payment/execute", | |
24 | :cancel_url => "http://localhost:3000/" }, | |
25 | | |
26 | # ###Transaction | |
27 | # A transaction defines the contract of a | |
28 | # payment - what is the payment for and who | |
29 | # is fulfilling it. | |
30 | :transactions => [{ | |
31 | | |
32 | # Item List | |
33 | :item_list => { | |
34 | :items => [{ | |
35 | :name => "item", | |
36 | :sku => "item", | |
37 | :price => "5", | |
38 | :currency => "USD", | |
39 | :quantity => 1 }]}, | |
40 | | |
41 | # ###Amount | |
42 | # Let's you specify a payment amount. | |
43 | :amount => { | |
44 | :total => "5", | |
45 | :currency => "USD" }, | |
46 | :description => "This is the payment transaction description." }]}) | |
47 | | |
48 | # Create Payment and return status | |
49 | if @payment.create | |
50 | # Redirect the user to given approval url | |
51 | @redirect_url = @payment.links.find{|v| v.rel == "approval_url" }.href | |
52 | logger.info "Payment[#{@payment.id}]" | |
53 | logger.info "Redirect: #{@redirect_url}" | |
54 | else | |
55 | logger.error @payment.error.inspect | |
56 | end | |
57 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/execute.rb: | |
-------------------------------------------------------------------------------- | |
1 | # # Execute an approved PayPal payment | |
2 | # Use this call to execute (complete) a PayPal payment that has been approved by the payer. | |
3 | # You can optionally update transaction information by passing in one or more transactions. | |
4 | # API used: /v1/payments/payment | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | # ID of the payment. This ID is provided when creating payment. | |
10 | payment_id = ENV["PAYMENT_ID"] || "PAY-83Y70608H1071210EKES5UNA" | |
11 | @payment = Payment.find(payment_id) | |
12 | | |
13 | # PayerID is required to approve the payment. | |
14 | if @payment.execute( :payer_id => ENV["PAYER_ID"] || "DUFRQ8GWYMJXC" ) # return true or false | |
15 | logger.info "Payment[#{@payment.id}] execute successfully" | |
16 | else | |
17 | logger.error @payment.error.inspect | |
18 | end | |
19 | | |
20 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/find.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #GetPayment Sample | |
2 | # This sample code demonstrates how you can retrieve | |
3 | # the details of a payment resource. | |
4 | # API used: /v1/payments/payment/{payment-id} | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | begin | |
10 | # Retrieve the payment object by calling the | |
11 | # `find` method | |
12 | # on the Payment class by passing Payment ID | |
13 | @payment = Payment.find("PAY-0XL713371A312273YKE2GCNI") | |
14 | logger.info "Got Payment Details for Payment[#{@payment.id}]" | |
15 | | |
16 | rescue ResourceNotFound => err | |
17 | # It will throw ResourceNotFound exception if the payment not found | |
18 | logger.error "Payment Not Found" | |
19 | end | |
20 | | |
-------------------------------------------------------------------------------- | |
/samples/payment/update.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #GetPayment Sample | |
2 | # This sample code demonstrates how you can retrieve | |
3 | # the details of a payment resource. | |
4 | # API used: /v1/payments/payment/{payment-id} | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | begin | |
10 | # Retrieve the payment object by calling the | |
11 | # `find` method | |
12 | # on the Payment class by passing Payment ID | |
13 | @payment = Payment.find("PAY-6BB05346FG6727110KYZEGNQ") | |
14 | logger.info "Got Payment Details for Payment[#{@payment.id}]" | |
15 | updated_payment = { | |
16 | :op=> "replace", | |
17 | :path=> "/transactions/0/amount", | |
18 | :value=> { | |
19 | :total=> "40.00", | |
20 | :currency=> "BRL", | |
21 | :details=> {:shipping=>"25.00", :subtotal=>"15.00"} | |
22 | } | |
23 | } | |
24 | patch_request = Patch.new(updated_payment) | |
25 | @payment.update(updated_payment) | |
26 | | |
27 | rescue ResourceNotFound => err | |
28 | # It will throw ResourceNotFound exception if the payment not found | |
29 | logger.error "Payment Not Found" | |
30 | end | |
31 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/cancel.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | # Create a Single Synchronous Payout with Invalid (Unclaimable Email Address) | |
10 | @payout_batch = RunSample.run('payouts/createSync.rb', '@payout_batch') | |
11 | | |
12 | # Cancel the item | |
13 | @payout_item_detail= PayoutItem.cancel(@payout_batch.items[0].payout_item_id) | |
14 | logger.info "Cancelled Unclaimed Payout with Item ID [#{@payout_item_detail.payout_item_id}]" | |
15 | | |
16 | rescue ResourceNotFound => err | |
17 | logger.error "Payout Item could not be cancelled" | |
18 | end | |
19 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/create.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require 'securerandom' | |
3 | require './runner.rb' | |
4 | | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | @payout = Payout.new({ | |
9 | :sender_batch_header => { | |
10 | :sender_batch_id => SecureRandom.hex(8), | |
11 | :email_subject => 'You have a Payout!' | |
12 | }, | |
13 | :items => [ | |
14 | { | |
15 | :recipient_type => 'EMAIL', | |
16 | :amount => { | |
17 | :value => '1.0', | |
18 | :currency => 'USD' | |
19 | }, | |
20 | :note => 'Thanks for your patronage!', | |
21 | :sender_item_id => '2014031400023', | |
22 | :receiver => '[email protected]' | |
23 | } | |
24 | ] | |
25 | }) | |
26 | begin | |
27 | @payout_batch = @payout.create | |
28 | logger.info "Created Payout with [#{@payout_batch.batch_header.payout_batch_id}]" | |
29 | rescue ResourceNotFound => err | |
30 | logger.error @payout.error.inspect | |
31 | end | |
32 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/createSync.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require 'securerandom' | |
3 | require './runner.rb' | |
4 | | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | @payouts = Payout.new({ | |
9 | :sender_batch_header => { | |
10 | :sender_batch_id => SecureRandom.hex(8), | |
11 | :email_subject => 'You have a Payout!' | |
12 | }, | |
13 | :items => [ | |
14 | { | |
15 | :recipient_type => 'EMAIL', | |
16 | :amount => { | |
17 | :value => '1.0', | |
18 | :currency => 'USD' | |
19 | }, | |
20 | :note => 'Thanks for your patronage!', | |
21 | :sender_item_id => '2014031400023', | |
22 | :receiver => '[email protected]' | |
23 | } | |
24 | ] | |
25 | }) | |
26 | begin | |
27 | @payout_batch = @payouts.create(true) | |
28 | logger.info "Created Synchronous Payout with [#{@payout_batch.batch_header.payout_batch_id}]" | |
29 | rescue ResourceNotFound => err | |
30 | logger.error @payouts.error.inspect | |
31 | end | |
32 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/createVenmo.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require 'securerandom' | |
3 | require './runner.rb' | |
4 | | |
5 | include PayPal::SDK::REST | |
6 | include PayPal::SDK::Core::Logging | |
7 | | |
8 | @payout = Payout.new({ | |
9 | :sender_batch_header => { | |
10 | :sender_batch_id => SecureRandom.hex(8), | |
11 | :email_subject => 'You have a Payout!' | |
12 | }, | |
13 | :items => [ | |
14 | { | |
15 | :recipient_type => 'PHONE', | |
16 | :amount => { | |
17 | :value => '1.0', | |
18 | :currency => 'USD' | |
19 | }, | |
20 | :note => 'Thanks for your patronage!', | |
21 | :sender_item_id => '2014031400023', | |
22 | :receiver => '5551232368', | |
23 | :recipient_wallet => 'VENMO' | |
24 | } | |
25 | ] | |
26 | }) | |
27 | begin | |
28 | @payout_batch = @payout.create | |
29 | logger.info "Created Venmo Payout with [#{@payout_batch.batch_header.payout_batch_id}]" | |
30 | rescue ResourceNotFound => err | |
31 | logger.error @payout.error.inspect | |
32 | end | |
33 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/get_batch_status.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | | |
4 | include PayPal::SDK::REST | |
5 | include PayPal::SDK::Core::Logging | |
6 | | |
7 | begin | |
8 | | |
9 | # Create a Batch Payout. | |
10 | @payout_batch = RunSample.run('payouts/create.rb', '@payout_batch') | |
11 | | |
12 | # Get Payout Batch Status | |
13 | @payout_batch= Payout.get(@payout_batch.batch_header.payout_batch_id) | |
14 | logger.info "Got Payout Batch Status[#{@payout_batch.batch_header.payout_batch_id}]" | |
15 | | |
16 | rescue ResourceNotFound => err | |
17 | logger.error "Payout Batch not Found" | |
18 | end | |
19 | | |
-------------------------------------------------------------------------------- | |
/samples/payouts/get_item_status.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'paypal-sdk-rest' | |
2 | require './runner.rb' | |
3 | include PayPal::SDK::REST | |
4 | include PayPal::SDK::Core::Logging | |
5 | | |
6 | begin | |
7 | | |
8 | # Create a Batch Payout. | |
9 | @payout_batch = RunSample.run('payouts/get_batch_status.rb', '@payout_batch') | |
10 | | |
11 | # Get Payout Item Status | |
12 | @payout_item_details= PayoutItem.get(@payout_batch.items[0].payout_item_id) | |
13 | logger.info "Got Payout Item Status[#{@payout_item_details.payout_item_id}]" | |
14 | | |
15 | rescue ResourceNotFound => err | |
16 | logger.error "Payout Item not Found" | |
17 | end | |
18 | | |
-------------------------------------------------------------------------------- | |
/samples/public/images/edt-format-source-button.png: | |
-------------------------------------------------------------------------------- | |
https://raw.githubusercontent.com/codigofacilito/PayPal-Ruby-SDK/b116bacbf4c43f056193fcd9b62c3131d482065d/samples/public/images/edt-format-source-button.png | |
-------------------------------------------------------------------------------- | |
/samples/public/images/play_button.png: | |
-------------------------------------------------------------------------------- | |
https://raw.githubusercontent.com/codigofacilito/PayPal-Ruby-SDK/b116bacbf4c43f056193fcd9b62c3131d482065d/samples/public/images/play_button.png | |
-------------------------------------------------------------------------------- | |
/samples/runner.rb: | |
-------------------------------------------------------------------------------- | |
1 | # Run Samples on different scope | |
2 | module RunSample | |
3 | def self.logger | |
4 | PayPal::SDK::Core::Config.logger | |
5 | end | |
6 | | |
7 | def self.run(file, variable) | |
8 | object_binding = binding | |
9 | object_binding.eval(File.read("./#{file}")) | |
10 | object_binding.eval(variable) | |
11 | end | |
12 | end | |
13 | | |
-------------------------------------------------------------------------------- | |
/samples/sale/find.rb: | |
-------------------------------------------------------------------------------- | |
1 | # # Get Details of a Sale Transaction Sample | |
2 | # This sample code demonstrates how you can retrieve | |
3 | # details of completed Sale Transaction. | |
4 | # API used: /v1/payments/sale/{sale-id} | |
5 | require 'paypal-sdk-rest' | |
6 | include PayPal::SDK::REST | |
7 | include PayPal::SDK::Core::Logging | |
8 | | |
9 | begin | |
10 | # Get Sale object by passing sale id | |
11 | @sale = Sale.find("7DY409201T7922549") | |
12 | logger.info "Got Sale[#{@sale.id}]" | |
13 | rescue ResourceNotFound => err | |
14 | logger.error "Sale Not Found" | |
15 | end | |
16 | | |
-------------------------------------------------------------------------------- | |
/samples/sale/refund.rb: | |
-------------------------------------------------------------------------------- | |
1 | # #SaleRefund Sample | |
2 | # This sample code demonstrate how you can | |
3 | # process a refund on a sale transaction created | |
4 | # using the Payments API. | |
5 | # API used: /v1/payments/sale/{sale-id}/refund | |
6 | require 'paypal-sdk-rest' | |
7 | include PayPal::SDK::REST | |
8 | include PayPal::SDK::Core::Logging | |
9 | | |
10 | @sale = Sale.find("7DY409201T7922549") | |
11 | | |
12 | # Make Refund API call | |
13 | # Set amount only if the refund is partial | |
14 | @refund = @sale.refund_request({ | |
15 | :amount => { | |
16 | :total => "0.01", | |
17 | :currency => "USD" } }) | |
18 | | |
19 | # Check refund status | |
20 | if @refund.success? | |
21 | logger.info "Refund[#{@refund.id}] Success" | |
22 | else | |
23 | logger.error "Unable to Refund" | |
24 | logger.error @refund.error.inspect | |
25 | end | |
26 | | |
-------------------------------------------------------------------------------- | |
/samples/views/display_hash.haml: | |
-------------------------------------------------------------------------------- | |
1 | !!! | |
2 | %html | |
3 | %head | |
4 | %title PayPal Rest API Samples | |
5 | %body | |
6 | %a{:href => ".."} Back | |
7 | - if display_hash.success? | |
8 | %h3= header | |
9 | .display-hash | |
10 | = CodeRay.scan(MultiJson.dump(display_hash.to_hash, :pretty => true), "json").div | |
11 | - else | |
12 | %h3 Error while #{header} | |
13 | .display-hash | |
14 | = CodeRay.scan(MultiJson.dump(display_hash.error, :pretty => true), "json").div | |
15 | %a{:href => ".."} Back | |
16 | | |
17 | | |
-------------------------------------------------------------------------------- | |
/spec/README.md: | |
-------------------------------------------------------------------------------- | |
1 | How to run tests | |
2 | ================ | |
3 | | |
4 | The SDK tests are composed of two groups: unit test group and integration (functional) test group. Integration test group is by default set not to run. | |
5 | | |
6 | - run a single test | |
7 | - `$ bundle exec rspec <test-file>:<line-number>` | |
8 | - e.g., to run payment create test, | |
9 | - `$ bundle exec rspec spec/payments_examples_spec.rb:53` | |
10 | | |
11 | - run multiple tests in the same file | |
12 | - Add `:<line-number>` to the above command. For example, in order to execute payment create and payment list tests, | |
13 | - `$ bundle exec rspec spec/payments_examples_spec.rb:53:95` | |
14 | | |
15 | - run integration tests | |
16 | - This will set the 'integration' flag and will execute functional tests against sandbox. | |
17 | - `$ bundle exec rspec --tag integration` | |
18 | | |
19 | - run tests with a specific String | |
20 | - `$ bundle exec rspec -e "<string>"` | |
21 | - e.g., to run any tests with "Sa" in test description (for the time being, it will be "Sale" tests) | |
22 | - `$ bundle exec rspec -e "Sa"` | |
23 | | |
-------------------------------------------------------------------------------- | |
/spec/config/cert_key.pem: | |
-------------------------------------------------------------------------------- | |
1 | -----BEGIN RSA PRIVATE KEY----- | |
2 | MIICXAIBAAKBgQCx/rJhKDRYhs9WZj66FA+HidsrKwvep3g+YNbm1fXmjcg2rEmC | |
3 | kq71+Ftw9rx0Uz7vmg6vcsASUgOyeNG7mVB1SsXc6j+JAzZsmpzxHI0QKm+nBNTS | |
4 | OAp5NWn6NZQfu3BNAJ/Mok/iL3am2DWXV6dU74J66rBpwIJfzs9kmw8ZGwIDAQAB | |
5 | AoGAa/V1sCQ4i7FItLjTNv3P5X+h5W74hhXBguQttFj2Ct7YHwEknQPnBt2aaMve | |
6 | xhdvxtgELDpHcVU5VNifLU/yUg3+DSr/YkpBWOcNTCt1seW/z5s+jr2fQERQKbyf | |
7 | SXWMTqwrQ19iQoCPYaj7Drf68JhksQCaYN650g7+B/QmSBECQQDp6r75fzDtEWrr | |
8 | O4Sl9plK6CRLqQQ3LveAw4JV31N2UAqgAYtzRqD6K+SviAVtX9xxuv983qQxsfX4 | |
9 | ozE9sGXPAkEAwsxwR1s2Acuy10h3Xj6VtnFB3PpUrkSI9c9ZxF4CMf/+AS/b2UEe | |
10 | QhH60WHY8ccgKT/DoPWBcEu2o0f9nPw29QJBAI480zHNeMe/Hp+5iliM0fvtmxxy | |
11 | wwB3S8L9n4RuD0dTNpLDPbO0D/DvvdhKwtoWP2rcxbx9eaRKTYKKYUfcupsCQAkP | |
12 | SQmIjHJ47tBkZmjTsFLT4aRNYDLarSQBiMNBPAjnRwD3INpx1N5tx6SFUHmuMSi5 | |
13 | 9nc9888tNklRx9HNSSECQHgs9ExBpA6WbRVcgiizOKH7fmNxAB5f6TQ2W1QHMUb+ | |
14 | UhZpwuDelOIfzJAQUZGTZk8a8uVmyXU5hTf3ZDbrnJ8= | |
15 | -----END RSA PRIVATE KEY----- | |
16 | | |
17 | -----BEGIN CERTIFICATE----- | |
18 | MIICpjCCAg+gAwIBAgIDD96nMA0GCSqGSIb3DQEBBQUAMIGfMQswCQYDVQQGEwJV | |
19 | UzETMBEGA1UECBMKQ2FsaWZvcm5pYTERMA8GA1UEBxMIU2FuIEpvc2UxFTATBgNV | |
20 | BAoTDFBheVBhbCwgSW5jLjEWMBQGA1UECxQNc2FuZGJveF9jZXJ0czEbMBkGA1UE | |
21 | AxQSc2FuZGJveF9jYW1lcmNoYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwu | |
22 | Y29tMB4XDTA5MTAxNTA2Mzg1N1oXDTE5MTAxMzA2Mzg1N1owgYAxLTArBgNVBAMU | |
23 | JHBsYXRmb18xMjU1MTcwNjk0X2Jpel9hcGkxLmdtYWlsLmNvbTEiMCAGA1UEChMZ | |
24 | cGxhdGZvcm0gc2RrJ3MgVGVzdCBTdG9yZTERMA8GA1UEBxMIU2FuIEpvc2UxCzAJ | |
25 | BgNVBAgTAkNBMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC | |
26 | gYEAsf6yYSg0WIbPVmY+uhQPh4nbKysL3qd4PmDW5tX15o3INqxJgpKu9fhbcPa8 | |
27 | dFM+75oOr3LAElIDsnjRu5lQdUrF3Oo/iQM2bJqc8RyNECpvpwTU0jgKeTVp+jWU | |
28 | H7twTQCfzKJP4i92ptg1l1enVO+CeuqwacCCX87PZJsPGRsCAwEAAaMNMAswCQYD | |
29 | VR0TBAIwADANBgkqhkiG9w0BAQUFAAOBgQCgH3kwXMJtcAaCBQLKz5TGFogJp/C3 | |
30 | 06MvjYzdbDrx9Rjf/252UhD8dMPUP5FhU1KXduL+KIEYawPbDQ9+lV58JgM12R0p | |
31 | EhCODDI/lDvzbfxUnYgkJ5cnFhTZpcAqVzWuinUnG8jAL9XKiEyu/C73ePMPWPbt | |
32 | otoWi+Tk828Qlw== | |
33 | -----END CERTIFICATE----- | |
34 | | |
-------------------------------------------------------------------------------- | |
/spec/config/paypal.yml: | |
-------------------------------------------------------------------------------- | |
1 | test: &default | |
2 | client_id: AYSq3RDGsmBLJE-otTkBtM-jBRd1TCQwFf9RGfwddNXWz0uFU9ztymylOhRS | |
3 | client_secret: EGnHDxD_qRPdaLdZz8iCr8N7_MzF-YHPTkjs6NKYQvQSBngp4PTTVWkPZRbL | |
4 | username: jb-us-seller_api1.paypal.com | |
5 | password: WX4WTU3S8MY44S7F | |
6 | signature: AFcWxV21C7fd0v3bYYYRCpSSRl31A7yDhhsPUU2XhtMoZXsWHFxu-RWy | |
7 | app_id: APP-80W284485P519543T | |
8 | http_timeout: 120 | |
9 | mode: sandbox | |
10 | sandbox_email_address: [email protected] | |
11 | | |
12 | development: | |
13 | <<: *default | |
14 | | |
15 | with_authentication: | |
16 | <<: *default | |
17 | client_id: AYSq3RDGsmBLJE-otTkBtM-jBRd1TCQwFf9RGfwddNXWz0uFU9ztymylOhRS | |
18 | client_secret: EGnHDxD_qRPdaLdZz8iCr8N7_MzF-YHPTkjs6NKYQvQSBngp4PTTVWkPZRbL | |
19 | | |
20 | with_certificate: | |
21 | <<: *default | |
22 | username: platfo_1255170694_biz_api1.gmail.com | |
23 | password: 2DPPKUPKB7DQLXNR | |
24 | signature: | |
25 | cert_path: <%= File.expand_path("../cert_key.pem", __FILE__) %> | |
26 | app_id: APP-80W284485P519543T | |
27 | | |
28 | with_oauth_token: | |
29 | <<: *default | |
30 | token: 3rMSi3kCmK1Q3.BKxkH29I53R0TRLrSuCO..l8AMOAFM6cQhPTTrfQ | |
31 | token_secret: RuE1j8RNRlSuL5T-PSSpVWLvOlI | |
32 | | |
33 | with_proxy: | |
34 | <<: *default | |
35 | http_proxy: <%= ENV['http_proxy'] %> | |
36 | | |
-------------------------------------------------------------------------------- | |
/spec/config/sample_data.yml: | |
-------------------------------------------------------------------------------- | |
1 | ipn: | |
2 | valid_message: item_number=&residence_country=US&verify_sign=AFcWxV21C7fd0v3bYYYRCpSSRl31AXi5tzp0u2U-8QDyy.oC2A1Dhx04&address_country=United+States&address_city=San+Jose&address_status=unconfirmed&business=platfo_1255077030_biz%40gmail.com&payment_status=Pending&transaction_subject=&protection_eligibility=Ineligible&shipping=0.00&payer_id=934EKX9W68RRU&first_name=John&mc_fee=0.38&txn_id=5AL16697HX185734U&quantity=1&receiver_email=platfo_1255077030_biz%40gmail.com¬ify_version=3.7&txn_type=web_accept&mc_gross=1.00&payer_status=unverified&mc_currency=USD&test_ipn=1&custom=&payment_date=01%3A48%3A31+Dec+04%2C+2012+PST&payment_fee=0.38&charset=windows-1252&address_country_code=US&payment_gross=1.00&address_zip=95131&ipn_track_id=af0f53159f21e&address_state=CA&receipt_id=4050-1771-4106-3070&pending_reason=paymentreview&tax=0.00&handling_amount=0.00&item_name=&address_name=John+Doe&last_name=Doe&payment_type=instant&receiver_id=HZH2W8NPXUE5W&address_street=1+Main+St | |
3 | invalid_message: invalid=invalid | |
4 | | |
-------------------------------------------------------------------------------- | |
/spec/core/api/rest_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | | |
3 | describe PayPal::SDK::Core::API::REST do | |
4 | | |
5 | module PayPalRest | |
6 | class API < PayPal::SDK::Core::API::REST | |
7 | end | |
8 | end | |
9 | | |
10 | def create_api(*args) | |
11 | api = PayPalRest::API.new(*args) | |
12 | @log_file ||= File.open("spec/log/rest_http.log", "w") | |
13 | api.http.set_debug_output(@log_file) | |
14 | api | |
15 | end | |
16 | | |
17 | before :all do | |
18 | @api = create_api("v1/payments/", :with_authentication) | |
19 | @vault_api = create_api("v1/vault/", :with_authentication) | |
20 | end | |
21 | | |
22 | describe "Configuration" do | |
23 | it "create api with prefix" do | |
24 | api = create_api("v1/payments") | |
25 | expect(api.uri.path).to eql "/v1/payments" | |
26 | end | |
27 | | |
28 | it "service endpoint for sandbox" do | |
29 | api = create_api | |
30 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
31 | expect(api.token_endpoint).to eql "https://api.sandbox.paypal.com" | |
32 | api = create_api( :mode => "sandbox" ) | |
33 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
34 | expect(api.token_endpoint).to eql "https://api.sandbox.paypal.com" | |
35 | api = create_api( :mode => :sandbox ) | |
36 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
37 | expect(api.token_endpoint).to eql "https://api.sandbox.paypal.com" | |
38 | api = create_api( :mode => "invalid" ) | |
39 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
40 | expect(api.token_endpoint).to eql "https://api.sandbox.paypal.com" | |
41 | api = create_api( :mode => nil ) | |
42 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
43 | expect(api.token_endpoint).to eql "https://api.sandbox.paypal.com" | |
44 | end | |
45 | | |
46 | it "service endpoint for live" do | |
47 | api = create_api( :mode => "live" ) | |
48 | expect(api.service_endpoint).to eql "https://api.paypal.com" | |
49 | expect(api.token_endpoint).to eql "https://api.paypal.com" | |
50 | api = create_api( :mode => :live ) | |
51 | expect(api.service_endpoint).to eql "https://api.paypal.com" | |
52 | expect(api.token_endpoint).to eql "https://api.paypal.com" | |
53 | end | |
54 | | |
55 | it "override service endpoint" do | |
56 | api = create_api( :rest_endpoint => "https://testing.api.paypal.com" ) | |
57 | expect(api.service_endpoint).to eql "https://testing.api.paypal.com" | |
58 | expect(api.token_endpoint).to eql "https://testing.api.paypal.com" | |
59 | api = create_api( :endpoint => "https://testing.api.paypal.com" ) | |
60 | expect(api.service_endpoint).to eql "https://testing.api.paypal.com" | |
61 | expect(api.token_endpoint).to eql "https://testing.api.paypal.com" | |
62 | end | |
63 | | |
64 | it "override token endpoint" do | |
65 | api = create_api( :rest_token_endpoint => "https://testing.api.paypal.com" ) | |
66 | expect(api.service_endpoint).to eql "https://api.sandbox.paypal.com" | |
67 | expect(api.token_endpoint).to eql "https://testing.api.paypal.com" | |
68 | end | |
69 | end | |
70 | | |
71 | describe "Validation" do | |
72 | it "Invalid client_id and client_secret" do | |
73 | api = create_api(:with_authentication, :client_id => "XYZ", :client_secret => "ABC") | |
74 | expect { | |
75 | api.token | |
76 | }.to raise_error PayPal::SDK::Core::Exceptions::UnauthorizedAccess | |
77 | end | |
78 | | |
79 | it "Should handle expired token" do | |
80 | old_token = @api.token | |
81 | @api.token_hash[:expires_in] = 0 | |
82 | new_token = @api.token | |
83 | expect(@api.token_hash[:expires_in]).not_to eql 0 | |
84 | end | |
85 | | |
86 | it "Get token" do | |
87 | expect(@api.token).not_to be_nil | |
88 | end | |
89 | end | |
90 | | |
91 | describe "Success request", :integration => true do | |
92 | | |
93 | it "Create Payment" do | |
94 | response = @api.post("payment", { | |
95 | "intent" => "sale", | |
96 | "payer" => { | |
97 | "payment_method" => "credit_card", | |
98 | "funding_instruments" => [{ | |
99 | "credit_card" => { | |
100 | "type" => "visa", | |
101 | "number" => "4567516310777851", | |
102 | "expire_month" => "11", "expire_year" => "2018", | |
103 | "first_name" => "Joe", "last_name" => "Shopper" }}]}, | |
104 | "transactions" => [{ | |
105 | "amount" => { | |
106 | "total" => "7.47", | |
107 | "currency" => "USD" }}]}) | |
108 | expect(response["error"]).to be_nil | |
109 | end | |
110 | | |
111 | it "List Payments" do | |
112 | response = @api.get("payment", { "count" => 10 }) | |
113 | expect(response["error"]).to be_nil | |
114 | expect(response["count"]).not_to be_nil | |
115 | end | |
116 | | |
117 | it "Execute Payment" | |
118 | | |
119 | it "Create FundingInstrument" do | |
120 | new_funding_instrument = @vault_api.post("credit-card", { | |
121 | "type" => "visa", | |
122 | "number" => "4111111111111111", | |
123 | "expire_month" => "11", "expire_year" => "2018", | |
124 | "cvv2" => "874", | |
125 | "first_name" => "Joe", "last_name" => "Shopper" }) | |
126 | expect(new_funding_instrument["error"]).to be_nil | |
127 | expect(new_funding_instrument["id"]).not_to be_nil | |
128 | | |
129 | funding_instrument = @vault_api.get("credit-card/#{new_funding_instrument["id"]}") | |
130 | expect(funding_instrument["error"]).to be_nil | |
131 | expect(funding_instrument["id"]).to eql new_funding_instrument["id"] | |
132 | end | |
133 | | |
134 | end | |
135 | | |
136 | describe "Failure request", :integration => true do | |
137 | it "Invalid Resource ID" do | |
138 | expect { | |
139 | response = @api.get("payment/PAY-1234") | |
140 | }.to raise_error PayPal::SDK::Core::Exceptions::ResourceNotFound | |
141 | end | |
142 | | |
143 | it "Invalid parameters" do | |
144 | response = @api.post("payment") | |
145 | expect(response["error"]["name"]).to eql "VALIDATION_ERROR" | |
146 | end | |
147 | end | |
148 | | |
149 | describe "format response" do | |
150 | before :each do | |
151 | @response = instance_double(Net::HTTPResponse) | |
152 | allow(@response).to receive(:code) { "200" } | |
153 | allow(@response).to receive(:content_type) { "application/json" } | |
154 | end | |
155 | | |
156 | it "parses empty object JSON correctly" do | |
157 | allow(@response).to receive(:body) { "{}" } | |
158 | payload = { | |
159 | :response => @response | |
160 | } | |
161 | | |
162 | formatted_response = @api.format_response(payload) | |
163 | expect(formatted_response).to_not be_nil | |
164 | expect(formatted_response[:data]).to eq({}) | |
165 | end | |
166 | | |
167 | it "parses empty string JSON correctly" do | |
168 | allow(@response).to receive(:body) { '""' } | |
169 | payload = { | |
170 | :response => @response | |
171 | } | |
172 | | |
173 | formatted_response = @api.format_response(payload) | |
174 | expect(formatted_response).to_not be_nil | |
175 | expect(formatted_response[:data]).to eq("") | |
176 | end | |
177 | | |
178 | it "parses whitespace body correctly" do | |
179 | allow(@response).to receive(:body) { ' ' } | |
180 | payload = { | |
181 | :response => @response | |
182 | } | |
183 | | |
184 | formatted_response = @api.format_response(payload) | |
185 | expect(formatted_response).to_not be_nil | |
186 | expect(formatted_response[:data]).to eq({}) | |
187 | end | |
188 | | |
189 | it "parses nil body correctly" do | |
190 | allow(@response).to receive(:body) { nil } | |
191 | payload = { | |
192 | :response => @response | |
193 | } | |
194 | | |
195 | formatted_response = @api.format_response(payload) | |
196 | expect(formatted_response).to_not be_nil | |
197 | expect(formatted_response[:data]).to eq({}) | |
198 | end | |
199 | | |
200 | it "parses with whitespace around JSON correctly" do | |
201 | allow(@response).to receive(:body) { ' { "test": "value" } ' } | |
202 | payload = { | |
203 | :response => @response | |
204 | } | |
205 | | |
206 | formatted_response = @api.format_response(payload) | |
207 | expect(formatted_response).to_not be_nil | |
208 | expect(formatted_response[:data]).to eq({ "test" => "value" }) | |
209 | end | |
210 | end | |
211 | end | |
212 | | |
-------------------------------------------------------------------------------- | |
/spec/core/config_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | | |
3 | describe PayPal::SDK::Core::Config do | |
4 | | |
5 | Config = PayPal::SDK::Core::Config | |
6 | | |
7 | it "load configuration file and default environment" do | |
8 | expect { | |
9 | Config.load("spec/config/paypal.yml", "test") | |
10 | expect(Config.default_environment).to eql "test" | |
11 | }.not_to raise_error | |
12 | end | |
13 | | |
14 | it "Set default environment" do | |
15 | begin | |
16 | backup_default_environment = Config.default_environment | |
17 | Config.default_environment = "new_env" | |
18 | expect(Config.default_environment).to eql "new_env" | |
19 | ensure | |
20 | Config.default_environment = backup_default_environment | |
21 | end | |
22 | end | |
23 | | |
24 | it "Set configurations" do | |
25 | begin | |
26 | backup_configurations = Config.configurations | |
27 | Config.configurations = { Config.default_environment => { :username => "direct", :password => "direct" } } | |
28 | expect(Config.config.username).to eql "direct" | |
29 | expect(Config.config.password).to eql "direct" | |
30 | expect(Config.config.signature).to be_nil | |
31 | ensure | |
32 | Config.configurations = backup_configurations | |
33 | end | |
34 | end | |
35 | | |
36 | it "Configure with parameters" do | |
37 | begin | |
38 | backup_configurations = Config.configurations | |
39 | Config.configurations = nil | |
40 | Config.configure( :username => "Testing" ) | |
41 | expect(Config.config.username).to eql "Testing" | |
42 | expect(Config.config.app_id).to be_nil | |
43 | ensure | |
44 | Config.configurations = backup_configurations | |
45 | end | |
46 | end | |
47 | | |
48 | it "Configure with block" do | |
49 | begin | |
50 | backup_configurations = Config.configurations | |
51 | Config.configurations = nil | |
52 | Config.configure do |config| | |
53 | config.username = "Testing" | |
54 | end | |
55 | expect(Config.config.username).to eql "Testing" | |
56 | expect(Config.config.app_id).to be_nil | |
57 | ensure | |
58 | Config.configurations = backup_configurations | |
59 | end | |
60 | end | |
61 | | |
62 | it "Configure with default values" do | |
63 | begin | |
64 | backup_configurations = Config.configurations | |
65 | default_config = Config.config | |
66 | Config.configure do |config| | |
67 | config.username = "Testing" | |
68 | end | |
69 | expect(Config.config.username).to eql "Testing" | |
70 | expect(Config.config.app_id).not_to be_nil | |
71 | expect(Config.config.app_id).to eql default_config.app_id | |
72 | ensure | |
73 | Config.configurations = backup_configurations | |
74 | end | |
75 | end | |
76 | | |
77 | it "validate configuration" do | |
78 | config = Config.new( :username => "XYZ") | |
79 | expect { | |
80 | config.required!(:username) | |
81 | }.not_to raise_error | |
82 | expect { | |
83 | config.required!(:password) | |
84 | }.to raise_error "Required configuration(password)" | |
85 | expect { | |
86 | config.required!(:username, :password) | |
87 | }.to raise_error "Required configuration(password)" | |
88 | expect { | |
89 | config.required!(:password, :signature) | |
90 | }.to raise_error "Required configuration(password, signature)" | |
91 | end | |
92 | | |
93 | it "return default environment configuration" do | |
94 | expect(Config.config).to be_a Config | |
95 | end | |
96 | | |
97 | it "return configuration based on environment" do | |
98 | expect(Config.config(:development)).to be_a Config | |
99 | end | |
100 | | |
101 | it "override default configuration" do | |
102 | override_configuration = { :username => "test.example.com", :app_id => "test"} | |
103 | config = Config.config(override_configuration) | |
104 | | |
105 | expect(config.username).to eql(override_configuration[:username]) | |
106 | expect(config.app_id).to eql(override_configuration[:app_id]) | |
107 | end | |
108 | | |
109 | it "get cached config" do | |
110 | expect(Config.config(:test)).to eql Config.config(:test) | |
111 | expect(Config.config(:test)).not_to eql Config.config(:development) | |
112 | end | |
113 | | |
114 | it "should raise error on invalid environment" do | |
115 | expect { | |
116 | Config.config(:invalid_env) | |
117 | }.to raise_error "Configuration[invalid_env] NotFound" | |
118 | end | |
119 | | |
120 | it "set logger" do | |
121 | require 'logger' | |
122 | my_logger = Logger.new(STDERR) | |
123 | Config.logger = my_logger | |
124 | expect(Config.logger).to eql my_logger | |
125 | end | |
126 | | |
127 | it "Access PayPal::SDK methods" do | |
128 | expect(PayPal::SDK.configure).to eql PayPal::SDK::Core::Config.config | |
129 | expect(PayPal::SDK.logger).to eql PayPal::SDK::Core::Config.logger | |
130 | PayPal::SDK.logger = PayPal::SDK.logger | |
131 | expect(PayPal::SDK.logger).to eql PayPal::SDK::Core::Config.logger | |
132 | end | |
133 | | |
134 | describe "include Configuration" do | |
135 | class TestConfig | |
136 | include PayPal::SDK::Core::Configuration | |
137 | end | |
138 | | |
139 | it "Get default configuration" do | |
140 | test_object = TestConfig.new | |
141 | expect(test_object.config).to be_a Config | |
142 | end | |
143 | | |
144 | it "Change environment" do | |
145 | test_object = TestConfig.new | |
146 | test_object.set_config("test") | |
147 | expect(test_object.config).to eql Config.config("test") | |
148 | expect(test_object.config).not_to eql Config.config("development") | |
149 | end | |
150 | | |
151 | it "Override environment configuration" do | |
152 | test_object = TestConfig.new | |
153 | test_object.set_config("test", :username => "test") | |
154 | expect(test_object.config).not_to eql Config.config("test") | |
155 | end | |
156 | | |
157 | it "Override default/current configuration" do | |
158 | test_object = TestConfig.new | |
159 | test_object.set_config( :username => "test") | |
160 | expect(test_object.config.username).to eql "test" | |
161 | test_object.set_config( :password => "test") | |
162 | expect(test_object.config.password).to eql "test" | |
163 | expect(test_object.config.username).to eql "test" | |
164 | end | |
165 | | |
166 | it "Append ssl_options" do | |
167 | test_object = TestConfig.new | |
168 | test_object.set_config( :ssl_options => { :ca_file => "test_path" } ) | |
169 | expect(test_object.config.ssl_options[:ca_file]).to eql "test_path" | |
170 | test_object.set_config( :ssl_options => { :verify_mode => 1 } ) | |
171 | expect(test_object.config.ssl_options[:verify_mode]).to eql 1 | |
172 | expect(test_object.config.ssl_options[:ca_file]).to eql "test_path" | |
173 | end | |
174 | | |
175 | it "Set configuration without loading configuration File" do | |
176 | backup_configurations = Config.configurations | |
177 | begin | |
178 | Config.configurations = nil | |
179 | test_object = TestConfig.new | |
180 | expect { | |
181 | test_object.config | |
182 | }.to raise_error | |
183 | test_object.set_config( :username => "test" ) | |
184 | expect(test_object.config).to be_a Config | |
185 | ensure | |
186 | Config.configurations = backup_configurations | |
187 | end | |
188 | end | |
189 | | |
190 | end | |
191 | | |
192 | end | |
193 | | |
-------------------------------------------------------------------------------- | |
/spec/core/logging_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | require 'stringio' | |
3 | | |
4 | describe PayPal::SDK::Core::Logging do | |
5 | Logging = PayPal::SDK::Core::Logging | |
6 | | |
7 | class TestLogging | |
8 | include Logging | |
9 | end | |
10 | | |
11 | before :each do | |
12 | @logger_file = StringIO.new | |
13 | Logging.logger = Logger.new(@logger_file) | |
14 | @test_logging = TestLogging.new | |
15 | end | |
16 | | |
17 | it "get logger object" do | |
18 | expect(@test_logging.logger).to be_a Logger | |
19 | end | |
20 | | |
21 | it "write message to logger" do | |
22 | test_message = "Example log message!!!" | |
23 | @test_logging.logger.info(test_message) | |
24 | @logger_file.rewind | |
25 | expect(@logger_file.read).to match test_message | |
26 | end | |
27 | | |
28 | end | |
29 | | |
-------------------------------------------------------------------------------- | |
/spec/core/openid_connect_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | | |
3 | describe PayPal::SDK::OpenIDConnect do | |
4 | OpenIDConnect = PayPal::SDK::OpenIDConnect | |
5 | | |
6 | before :all do | |
7 | OpenIDConnect.set_config( :client_id => "client_id", :openid_redirect_uri => "http://google.com" ) | |
8 | end | |
9 | | |
10 | it "Validate user_agent" do | |
11 | expect(OpenIDConnect::API.user_agent).to match "PayPalSDK/openid-connect-ruby" | |
12 | end | |
13 | | |
14 | describe "generate_authorize_url" do | |
15 | | |
16 | it "generate autorize_url" do | |
17 | url = OpenIDConnect::Tokeninfo.authorize_url | |
18 | expect(url).to match "client_id=client_id" | |
19 | expect(url).to match Regexp.escape("redirect_uri=#{CGI.escape("http://google.com")}") | |
20 | expect(url).to match "scope=openid" | |
21 | end | |
22 | | |
23 | describe "sandbox" do | |
24 | before do | |
25 | PayPal::SDK.configure(:mode => "sandbox") | |
26 | end | |
27 | | |
28 | it "generates a sandbox authorize url" do | |
29 | url = OpenIDConnect::Tokeninfo.authorize_url | |
30 | expect(url).to match "sandbox.paypal.com" | |
31 | end | |
32 | end | |
33 | end | |
34 | | |
35 | it "Override authorize_url params" do | |
36 | url = OpenIDConnect.authorize_url( | |
37 | :client_id => "new_client_id", | |
38 | :redirect_uri => "http://example.com", | |
39 | :scope => "openid profile") | |
40 | expect(url).to match "client_id=new_client_id" | |
41 | expect(url).to match Regexp.escape("redirect_uri=#{CGI.escape("http://example.com")}") | |
42 | expect(url).to match Regexp.escape("scope=#{CGI.escape("openid profile")}") | |
43 | end | |
44 | | |
45 | it "Generate logout_url" do | |
46 | url = OpenIDConnect.logout_url | |
47 | expect(url).to match "logout=true" | |
48 | expect(url).to match Regexp.escape("redirect_uri=#{CGI.escape("http://google.com")}") | |
49 | expect(url).not_to match "id_token" | |
50 | end | |
51 | | |
52 | it "Override logout_url params" do | |
53 | url = OpenIDConnect.logout_url({ | |
54 | :redirect_uri => "http://example.com", | |
55 | :id_token => "testing" }) | |
56 | expect(url).to match Regexp.escape("redirect_uri=#{CGI.escape("http://example.com")}") | |
57 | expect(url).to match "id_token=testing" | |
58 | end | |
59 | | |
60 | describe "Validation" do | |
61 | it "Create token" do | |
62 | expect{ | |
63 | tokeninfo = OpenIDConnect::Tokeninfo.create("invalid-autorize-code") | |
64 | }.to raise_error PayPal::SDK::Core::Exceptions::BadRequest | |
65 | end | |
66 | | |
67 | it "Refresh token" do | |
68 | expect{ | |
69 | tokeninfo = OpenIDConnect::Tokeninfo.refresh("invalid-refresh-token") | |
70 | }.to raise_error PayPal::SDK::Core::Exceptions::BadRequest | |
71 | end | |
72 | | |
73 | it "Get userinfo" do | |
74 | expect{ | |
75 | userinfo = OpenIDConnect::Userinfo.get("invalid-access-token") | |
76 | }.to raise_error PayPal::SDK::Core::Exceptions::UnauthorizedAccess | |
77 | end | |
78 | end | |
79 | | |
80 | describe "Tokeninfo" do | |
81 | before do | |
82 | @tokeninfo = OpenIDConnect::Tokeninfo.new( :access_token => "test_access_token", | |
83 | :refresh_token => "test_refresh_token", | |
84 | :id_token => "test_id_token" ) | |
85 | end | |
86 | | |
87 | it "create" do | |
88 | allow(OpenIDConnect::Tokeninfo.api).to receive_messages( :post => { :access_token => "access_token" } ) | |
89 | tokeninfo = OpenIDConnect::Tokeninfo.create("authorize_code") | |
90 | expect(tokeninfo).to be_a OpenIDConnect::Tokeninfo | |
91 | expect(tokeninfo.access_token).to eql "access_token" | |
92 | end | |
93 | | |
94 | it "refresh" do | |
95 | allow(@tokeninfo.api).to receive_messages( :post => { :access_token => "new_access_token" } ) | |
96 | expect(@tokeninfo.access_token).to eql "test_access_token" | |
97 | @tokeninfo.refresh | |
98 | expect(@tokeninfo.access_token).to eql "new_access_token" | |
99 | end | |
100 | | |
101 | it "userinfo" do | |
102 | allow(@tokeninfo.api).to receive_messages( :post => { :name => "Testing" } ) | |
103 | userinfo = @tokeninfo.userinfo | |
104 | expect(userinfo).to be_a OpenIDConnect::Userinfo | |
105 | expect(userinfo.name).to eql "Testing" | |
106 | end | |
107 | | |
108 | describe "logout_url" do | |
109 | it "Generate logout_url" do | |
110 | url = @tokeninfo.logout_url | |
111 | expect(url).to match "id_token=test_id_token" | |
112 | expect(url).to match "logout=true" | |
113 | expect(url).to match Regexp.escape("redirect_uri=#{CGI.escape("http://google.com")}") | |
114 | end | |
115 | | |
116 | describe "sandbox" do | |
117 | before do | |
118 | PayPal::SDK.configure(:mode => "sandbox") | |
119 | end | |
120 | | |
121 | it "generates a sandbox logout url" do | |
122 | url = @tokeninfo.logout_url | |
123 | expect(url).to match "sandbox.paypal.com" | |
124 | end | |
125 | end | |
126 | end | |
127 | end | |
128 | | |
129 | describe "Userinfo" do | |
130 | it "get" do | |
131 | allow(OpenIDConnect::Userinfo.api).to receive_messages( :post => { :name => "Testing" } ) | |
132 | | |
133 | userinfo = OpenIDConnect::Userinfo.get("access_token") | |
134 | userinfo = OpenIDConnect::Userinfo.new( { :name => "Testing" } ) | |
135 | expect(userinfo).to be_a OpenIDConnect::Userinfo | |
136 | expect(userinfo.name).to eql "Testing" | |
137 | | |
138 | userinfo = OpenIDConnect::Userinfo.get( :access_token => "access_token" ) | |
139 | expect(userinfo).to be_a OpenIDConnect::Userinfo | |
140 | expect(userinfo.name).to eql "Testing" | |
141 | end | |
142 | | |
143 | it "get", :integration => true do | |
144 | api = PayPal::SDK::REST::API.new | |
145 | access_token = api.token_hash() | |
146 | userinfo = OpenIDConnect::Userinfo.get( access_token ) | |
147 | expect(userinfo).to be_a OpenIDConnect::Userinfo | |
148 | expect(userinfo.email).to eql "[email protected]" | |
149 | end | |
150 | end | |
151 | | |
152 | | |
153 | end | |
154 | | |
-------------------------------------------------------------------------------- | |
/spec/invoice_examples_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require "spec_helper" | |
2 | | |
3 | describe "Invoice", :integration => true do | |
4 | | |
5 | InvoiceAttributes = { | |
6 | "merchant_info" => { | |
7 | "email" => "[email protected]" | |
8 | }, | |
9 | "billing_info" => [ { "email" => "[email protected]" } ], | |
10 | "items" => [ | |
11 | { | |
12 | "name" => "Sutures", | |
13 | "quantity" => 100, | |
14 | "unit_price" => { | |
15 | "currency" => "USD", | |
16 | "value" => 5 | |
17 | } | |
18 | } | |
19 | ], | |
20 | "note" => "Medical Invoice 16 Jul, 2013 PST" | |
21 | } | |
22 | | |
23 | it "create invoice" do | |
24 | invoice = PayPal::SDK::REST::Invoice.new(InvoiceAttributes) | |
25 | expect(invoice.create).to be_truthy | |
26 | end | |
27 | | |
28 | it "list invoice" do | |
29 | history = PayPal::SDK::REST::Invoice.get_all( :total_count_required =>true ) | |
30 | expect(history.total_count).not_to be_nil | |
31 | end | |
32 | | |
33 | it "get invoice" do | |
34 | invoice = PayPal::SDK::REST::Invoice.find("INV2-6KYE-67GV-8AJR-SAER") | |
35 | expect(invoice).to be_a PayPal::SDK::REST::Invoice | |
36 | expect(invoice.id).to eql "INV2-6KYE-67GV-8AJR-SAER" | |
37 | end | |
38 | end | |
39 | | |
-------------------------------------------------------------------------------- | |
/spec/log/.gitkeep: | |
-------------------------------------------------------------------------------- | |
https://raw.githubusercontent.com/codigofacilito/PayPal-Ruby-SDK/b116bacbf4c43f056193fcd9b62c3131d482065d/spec/log/.gitkeep | |
-------------------------------------------------------------------------------- | |
/spec/payouts_examples_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require "spec_helper" | |
2 | | |
3 | describe "Payouts", :integration => true do | |
4 | | |
5 | PayoutVenmoAttributes = { | |
6 | :sender_batch_header => { | |
7 | :sender_batch_id => SecureRandom.hex(8) | |
8 | }, | |
9 | :items => [ | |
10 | { | |
11 | :recipient_type => 'PHONE', | |
12 | :amount => { | |
13 | :value => '1.0', | |
14 | :currency => 'USD' | |
15 | }, | |
16 | :note => 'Thanks for your patronage!', | |
17 | :sender_item_id => '2014031400023', | |
18 | :receiver => '5551232368', | |
19 | :recipient_wallet => 'VENMO' | |
20 | } | |
21 | ] | |
22 | } | |
23 | | |
24 | PayoutAttributes = { | |
25 | :sender_batch_header => { | |
26 | :sender_batch_id => SecureRandom.hex(8), | |
27 | :email_subject => 'You have a Payout!' | |
28 | }, | |
29 | :items => [ | |
30 | { | |
31 | :recipient_type => 'EMAIL', | |
32 | :amount => { | |
33 | :value => '1.0', | |
34 | :currency => 'USD' | |
35 | }, | |
36 | :note => 'Thanks for your patronage!', | |
37 | :sender_item_id => '2014031400023', | |
38 | :receiver => '[email protected]' | |
39 | } | |
40 | ] | |
41 | } | |
42 | | |
43 | it "create venmo payout" do | |
44 | $payout = PayPal::SDK::REST::Payout.new(PayoutVenmoAttributes) | |
45 | $payout_batch = $payout.create | |
46 | expect($payout_batch).to be_truthy | |
47 | end | |
48 | | |
49 | it "create payout sync" do | |
50 | $payout = PayPal::SDK::REST::Payout.new(PayoutAttributes) | |
51 | $payout_batch = $payout.create(true) | |
52 | expect($payout_batch).to be_truthy | |
53 | end | |
54 | | |
55 | it "get payout batch status" do | |
56 | $result = PayPal::SDK::REST::Payout.get($payout_batch.batch_header.payout_batch_id) | |
57 | expect($result).to be_a PayPal::SDK::REST::PayoutBatch | |
58 | expect($payout_batch.batch_header.payout_batch_id).to eql $result.batch_header.payout_batch_id | |
59 | end | |
60 | | |
61 | it "get payout item status" do | |
62 | $payout_item_details= PayoutItem.get($payout_batch.items[0].payout_item_id) | |
63 | expect($payout_item_details).to be_a PayPal::SDK::REST::PayoutItemDetails | |
64 | expect($payout_item_details.payout_item_id).to eql $payout_batch.items[0].payout_item_id | |
65 | end | |
66 | | |
67 | it "cancel unclaimed payouts" do | |
68 | $payout_item_details= PayoutItem.cancel($payout_batch.items[0].payout_item_id) | |
69 | expect($payout_item_details).to be_a PayPal::SDK::REST::PayoutItemDetails | |
70 | expect($payout_item_details.payout_item_id).to eql $payout_batch.items[0].payout_item_id | |
71 | expect($payout_item_details.transaction_status).to eql 'RETURNED' | |
72 | end | |
73 | | |
74 | end | |
75 | | |
-------------------------------------------------------------------------------- | |
/spec/rest/data_types_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::REST::DataTypes | |
2 | describe Base do | |
3 | let(:error_object) { | |
4 | { | |
5 | "name" => "INVALID_EXPERIENCE_PROFILE_ID", | |
6 | "message" => "The requested experience profile ID was not found", | |
7 | "information_link" => "https://developer.paypal.com/docs/api/#INVALID_EXPERIENCE_PROFILE_ID", | |
8 | "debug_id" => "1562931a79fd2" | |
9 | } | |
10 | } | |
11 | | |
12 | context '#raise_error!' do | |
13 | context 'when there is error' do | |
14 | subject { described_class.new(error: error_object) } | |
15 | | |
16 | it 'raises error on request with all API information' do | |
17 | expect { subject.raise_error! } | |
18 | .to raise_error { |err| | |
19 | expect(err).to be_a(PayPal::SDK::Core::Exceptions::UnsuccessfulApiCall) | |
20 | expect(err.message).to eq("The requested experience profile ID was not found") | |
21 | expect(err.api_error).to eq(error_object) | |
22 | } | |
23 | end | |
24 | end | |
25 | | |
26 | context 'when there is no error' do | |
27 | subject { described_class.new(error: nil) } | |
28 | | |
29 | it { expect { subject.raise_error! }.not_to raise_error } | |
30 | end | |
31 | end | |
32 | | |
33 | context '.raise_on_api_error' do | |
34 | let(:klass) { | |
35 | Class.new(described_class) do | |
36 | def some_call | |
37 | end | |
38 | | |
39 | raise_on_api_error :some_call | |
40 | end | |
41 | } | |
42 | | |
43 | subject { klass.new } | |
44 | | |
45 | context 'when call is successful' do | |
46 | before { | |
47 | expect(subject).to receive(:some_call).and_return(true) | |
48 | } | |
49 | it { expect { subject.some_call! }.not_to raise_error } | |
50 | end | |
51 | | |
52 | context 'when call is unsuccessful' do | |
53 | before { | |
54 | expect(subject).to receive(:some_call).and_return(false) | |
55 | expect(subject).to receive(:error).twice.and_return(error_object) | |
56 | } | |
57 | | |
58 | it { expect { subject.some_call! }.to raise_error(PayPal::SDK::Core::Exceptions::UnsuccessfulApiCall) } | |
59 | end | |
60 | end | |
61 | end | |
62 | end | |
63 | | |
-------------------------------------------------------------------------------- | |
/spec/rest/error_hash_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | module PayPal::SDK::REST | |
2 | describe ErrorHash do | |
3 | it 'converts Hashes to ErrorHashes' do | |
4 | hash = ErrorHash.convert({ | |
5 | nested_hash: { bing: 'bong' }, | |
6 | empty_array: [], | |
7 | array_with_hashes: [ | |
8 | { foo: 'boo' }, | |
9 | { biz: 'boz' } | |
10 | ], | |
11 | array_without_hashes: [1, 2, 3], | |
12 | nilly: nil, | |
13 | stringy: 'cheese' | |
14 | }) | |
15 | | |
16 | expect(hash).to be_a(ErrorHash) | |
17 | expect(hash.nested_hash).to be_a(ErrorHash) | |
18 | expect(hash.empty_array).to eq([]) | |
19 | expect(hash.array_with_hashes[0]).to be_a(ErrorHash) | |
20 | expect(hash.array_with_hashes[1]).to be_a(ErrorHash) | |
21 | expect(hash.array_without_hashes).to eq([1, 2, 3]) | |
22 | expect(hash.nilly).to be_nil | |
23 | expect(hash.stringy).to eq('cheese') | |
24 | end | |
25 | | |
26 | it 'can access string keys as properties, strings, or symbols' do | |
27 | hash = ErrorHash.convert({ 'foo' => 5 }) | |
28 | hash['boo'] = 'grue' | |
29 | | |
30 | expect(hash.foo).to eq(5) | |
31 | expect(hash['foo']).to eq(5) | |
32 | expect(hash[:foo]).to eq(5) | |
33 | expect(hash.boo).to eq('grue') | |
34 | expect(hash['boo']).to eq('grue') | |
35 | expect(hash[:boo]).to eq('grue') | |
36 | end | |
37 | | |
38 | it 'can access symbol keys as properties, strings, or symbols' do | |
39 | hash = ErrorHash.convert({ :foo => 5 }) | |
40 | hash[:boo] = 'grue' | |
41 | | |
42 | expect(hash.foo).to eq(5) | |
43 | expect(hash['foo']).to eq(5) | |
44 | expect(hash[:foo]).to eq(5) | |
45 | expect(hash.boo).to eq('grue') | |
46 | expect(hash['boo']).to eq('grue') | |
47 | expect(hash[:boo]).to eq('grue') | |
48 | end | |
49 | | |
50 | it 'converts Hashes to ErrorHashes on assignment' do | |
51 | hash = ErrorHash.new | |
52 | hash['foo'] = { bing: 'bong' } | |
53 | | |
54 | expect(hash['foo']).to be_a(ErrorHash) | |
55 | expect(hash['foo'].bing).to eq('bong') | |
56 | end | |
57 | | |
58 | it 'converts Hashes inside of Arrays to ErrorHashes on assignment' do | |
59 | hash = ErrorHash.new | |
60 | hash['foo'] = [{ bing: 'bong' }] | |
61 | | |
62 | expect(hash['foo'][0]).to be_a(ErrorHash) | |
63 | expect(hash['foo'][0].bing).to eq('bong') | |
64 | end | |
65 | | |
66 | it "doesn't convert Hashes inside of Arrays if the first element of the array isn't a Hash" do | |
67 | hash = ErrorHash.new | |
68 | hash['foo'] = [100, { bing: 'bong' }] | |
69 | | |
70 | expect(hash['foo'][1]).to be_a(Hash) | |
71 | expect(hash['foo'][1]).not_to be_a(ErrorHash) | |
72 | end | |
73 | | |
74 | it 'gets and sets numbers and strings' do | |
75 | hash = ErrorHash.new | |
76 | hash['foo'] = 123 | |
77 | hash['boo'] = 'baa' | |
78 | | |
79 | expect(hash['foo']).to eq(123) | |
80 | expect(hash['boo']).to eq('baa') | |
81 | end | |
82 | end | |
83 | end | |
84 | | |
-------------------------------------------------------------------------------- | |
/spec/spec_helper.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'bundler/setup' | |
2 | | |
3 | if ENV['COVERAGE'] | |
4 | require 'simplecov' | |
5 | require 'coveralls' | |
6 | Coveralls.wear! | |
7 | SimpleCov.start do | |
8 | add_filter "/spec/" | |
9 | end | |
10 | end | |
11 | | |
12 | Bundler.require :default, :test | |
13 | PayPal::SDK::Core::Config.load(File.expand_path('../config/paypal.yml', __FILE__), 'test') | |
14 | | |
15 | require 'paypal-sdk-rest' | |
16 | | |
17 | include PayPal::SDK::REST | |
18 | include PayPal::SDK::Core::Logging | |
19 | | |
20 | require 'logger' | |
21 | PayPal::SDK.load('spec/config/paypal.yml', 'test') | |
22 | PayPal::SDK.logger = Logger.new(STDERR) | |
23 | | |
24 | Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f } | |
25 | | |
26 | # Set logger for http | |
27 | http_log = File.open(File.expand_path('../log/http.log', __FILE__), "w") | |
28 | Payment.api.http.set_debug_output(http_log) | |
29 | | |
30 | RSpec.configure do |config| | |
31 | config.filter_run_excluding :integration => true | |
32 | config.filter_run_excluding :disabled => true | |
33 | config.include SampleData | |
34 | # config.include PayPal::SDK::REST::DataTypes | |
35 | end | |
36 | | |
37 | WebMock.allow_net_connect! | |
38 | | |
-------------------------------------------------------------------------------- | |
/spec/subscription_examples_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | | |
3 | describe "Subscription" do | |
4 | | |
5 | PlanAttributes = { | |
6 | "name" => "T-Shirt of the Month Club Plan", | |
7 | "description" => "Template creation.", | |
8 | "type" => "fixed", | |
9 | "payment_definitions" => [ | |
10 | { | |
11 | "name" => "Regular Payments", | |
12 | "type" => "REGULAR", | |
13 | "frequency" => "MONTH", | |
14 | "frequency_interval" => "2", | |
15 | "amount" => { | |
16 | "value" => "100", | |
17 | "currency" => "USD" | |
18 | }, | |
19 | "cycles" => "12", | |
20 | "charge_models" => [ | |
21 | { | |
22 | "type" => "SHIPPING", | |
23 | "amount" => { | |
24 | "value" => "10", | |
25 | "currency" => "USD" | |
26 | } | |
27 | }, | |
28 | { | |
29 | "type" => "TAX", | |
30 | "amount" => { | |
31 | "value" => "12", | |
32 | "currency" => "USD" | |
33 | } | |
34 | } | |
35 | ] | |
36 | } | |
37 | ], | |
38 | "merchant_preferences" => { | |
39 | "setup_fee" => { | |
40 | "value" => "1", | |
41 | "currency" => "USD" | |
42 | }, | |
43 | "return_url" => "http://www.return.com", | |
44 | "cancel_url" => "http://www.cancel.com", | |
45 | "auto_bill_amount" => "YES", | |
46 | "initial_fail_amount_action" => "CONTINUE", | |
47 | "max_fail_attempts" => "0" | |
48 | } | |
49 | } | |
50 | | |
51 | AgreementAttributes = { | |
52 | "name" => "T-Shirt of the Month Club Agreement", | |
53 | "description" => "Agreement for T-Shirt of the Month Club Plan", | |
54 | "start_date" => "2015-02-19T00:37:04Z", | |
55 | "payer" => { | |
56 | "payment_method" => "paypal" | |
57 | }, | |
58 | "shipping_address" => { | |
59 | "line1" => "111 First Street", | |
60 | "city" => "Saratoga", | |
61 | "state" => "CA", | |
62 | "postal_code" => "95070", | |
63 | "country_code" => "US" | |
64 | } | |
65 | } | |
66 | | |
67 | describe "BillingPlan", :integration => true do | |
68 | it "Create" do | |
69 | # create access token and then create a plan | |
70 | $api = API.new | |
71 | plan = Plan.new(PlanAttributes.merge( :token => $api.token )) | |
72 | expect(Plan.api).not_to eql plan.api | |
73 | plan.create | |
74 | | |
75 | # make sure the transaction was successful | |
76 | $plan_id = plan.id | |
77 | expect(plan.error).to be_nil | |
78 | expect(plan.id).not_to be_nil | |
79 | end | |
80 | | |
81 | it "Update" do | |
82 | # create a new plan to update | |
83 | plan = Plan.new(PlanAttributes) | |
84 | expect(plan.create).to be_truthy | |
85 | | |
86 | # set up a patch request | |
87 | patch = Patch.new | |
88 | patch.op = "replace" | |
89 | patch.path = "/"; | |
90 | patch.value = { :state => "ACTIVE" } | |
91 | | |
92 | # the patch request should be successful | |
93 | expect(plan.update( patch )).to be_truthy | |
94 | end | |
95 | | |
96 | it "List" do | |
97 | # list all billing plans | |
98 | plan_list = Plan.all | |
99 | expect(plan_list.error).to be_nil | |
100 | expect(plan_list.plans.count).to be > 1 | |
101 | end | |
102 | | |
103 | it "Delete" do | |
104 | # create a plan to delete | |
105 | plan = Plan.new(PlanAttributes.merge( :token => $api.token )) | |
106 | plan.create | |
107 | plan_id = plan.id | |
108 | | |
109 | # construct a patch object that will be used for deletion | |
110 | patch = Patch.new | |
111 | patch.op = "replace" | |
112 | patch.path = "/" | |
113 | patch.value = { "state" => "DELETED" } | |
114 | | |
115 | # send delete request | |
116 | plan.update(patch) | |
117 | | |
118 | # make sure the plan has been deleted | |
119 | plan = Plan.find(plan_id) | |
120 | expect(plan.id).not_to eq plan_id | |
121 | end | |
122 | end | |
123 | | |
124 | describe "BillingAgreement", :integration => true do | |
125 | | |
126 | it "Create" do | |
127 | # first, create an active plan to be added to agreement | |
128 | api = API.new | |
129 | plan = Plan.new(PlanAttributes) | |
130 | expect(plan.create).to be_truthy | |
131 | | |
132 | # first, create an agreement | |
133 | $agreement = Agreement.new(AgreementAttributes) | |
134 | $agreement.plan = Plan.new( :id => "P-1K47639161110773GYDKTWIA" ) | |
135 | $agreement.shipping_address = nil | |
136 | | |
137 | # verify newly created agreement | |
138 | expect($agreement.create).to be_truthy | |
139 | expect($agreement.id).to be_nil | |
140 | expect($agreement.token).not_to be_nil | |
141 | expect($agreement.name).to eq("T-Shirt of the Month Club Agreement") | |
142 | end | |
143 | | |
144 | ##################################### | |
145 | # The following tests are disabled due to them requiring an active billing agreement or buyer's approval. | |
146 | # Most of them require an agreement ID, which is returned after executing agreement. | |
147 | # And agreement execution requires buyer's approval. | |
148 | ##################################### | |
149 | | |
150 | xit "Execute" do | |
151 | # Use this call to execute an agreement after the buyer approves it. | |
152 | expect($agreement.execute).to be_truthy | |
153 | end | |
154 | | |
155 | xit "Get" do | |
156 | # this call needs an agreement ID of the agreement to be retrieved | |
157 | agreement = Agreement.find($agreement.id) | |
158 | expect(agreement.id).to eq($agreement.id) | |
159 | expect(agreement.description).to eq("Agreement for T-Shirt of the Month Club Plan") | |
160 | expect(agreement.start_date).to eq("2015-02-19T00:37:04Z") | |
161 | expect(agreement.plan).not_to be_nil | |
162 | end | |
163 | | |
164 | xit "Update" do | |
165 | # get the agreement to update | |
166 | api = API.new | |
167 | plan = Plan.new(PlanAttributes) | |
168 | expect(plan.create).to be_truthy | |
169 | | |
170 | # first, create an agreement | |
171 | agreement = Agreement.new(AgreementAttributes) | |
172 | agreement.plan = Plan.new( :id => "P-1K47639161110773GYDKTWIA" ) | |
173 | expect(agreement.create).to be_truthy | |
174 | | |
175 | | |
176 | # create an update for the agreement | |
177 | random_string = (0...8).map { (65 + rand(26)).chr }.join | |
178 | patch = Patch.new | |
179 | patch.op = "replace" | |
180 | patch.path = "/" | |
181 | patch.value = { "description" => random_string } | |
182 | | |
183 | # send update request | |
184 | response = agreement.update(patch) | |
185 | | |
186 | # verify the agreement update was successful | |
187 | expect(response).to be_truthy | |
188 | updated_agreement = Agreement.get($agreement.id) | |
189 | expect(updated_agreement.id).to eq($agreement.id) | |
190 | expect(random_string).to eq(updated_agreement.description) | |
191 | end | |
192 | | |
193 | xit "Suspend" do | |
194 | # set the id of an active agreement here | |
195 | agreement_id = "" | |
196 | agreement = Agreement.find(agreement_id) | |
197 | | |
198 | state_descriptor = AgreementStateDescriptor.new( :note => "Suspending the agreement" ) | |
199 | expect( agreement.suspend(state_descriptor) ).to be_truthy | |
200 | end | |
201 | | |
202 | xit "Reactivate" do | |
203 | # set the id of a suspended agreement here | |
204 | agreement_id = "" | |
205 | agreement = Agreement.find(agreement_id) | |
206 | | |
207 | state_descriptor = AgreementStateDescriptor.new( :note => "Re-activating the agreement" ) | |
208 | expect( agreement.re_activate(state_descriptor) ).to be_truthy | |
209 | end | |
210 | | |
211 | xit "Search" do | |
212 | transactions = Agreement.transactions($agreement.id, "2015-01-01", "2015-01-10") | |
213 | expect(transactions).not_to be_nil | |
214 | expect(transactions.agreement_transaction_list).not_to be_empty | |
215 | end | |
216 | | |
217 | xit "Cancel" do | |
218 | # set the id of an agreement here | |
219 | agreement_id = "" | |
220 | agreement = Agreement.find(agreement_id) | |
221 | | |
222 | state_descriptor = AgreementStateDescriptor.new( :note => "Cancelling the agreement" ) | |
223 | expect( agreement.cancel(state_descriptor) ).to be_truthy | |
224 | end | |
225 | | |
226 | end | |
227 | end | |
228 | | |
-------------------------------------------------------------------------------- | |
/spec/support/sample_data.rb: | |
-------------------------------------------------------------------------------- | |
1 | module SampleData | |
2 | def samples | |
3 | @@samples ||= YAML.load(File.read(File.expand_path("../../config/sample_data.yml", __FILE__))) | |
4 | end | |
5 | end | |
6 | | |
-------------------------------------------------------------------------------- | |
/spec/web_profile_examples_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require 'spec_helper' | |
2 | | |
3 | describe "WebProfiles" do | |
4 | | |
5 | WebProfileAttributes = { | |
6 | "name" => "YeowZa! T-Shirt Shop", | |
7 | "presentation" => { | |
8 | "brand_name" => "YeowZa! Paypal", | |
9 | "logo_image" => "http://www.yeowza.com", | |
10 | "locale_code" => "US" | |
11 | }, | |
12 | "input_fields" => { | |
13 | "allow_note" => true, | |
14 | "no_shipping" => 0, | |
15 | "address_override" => 1 | |
16 | }, | |
17 | "flow_config" => { | |
18 | "landing_page_type" => "billing", | |
19 | "bank_txn_pending_url" => "http://www.yeowza.com" | |
20 | } | |
21 | } | |
22 | | |
23 | describe "Examples" do | |
24 | describe "WebProfile", :integration => true do | |
25 | it "Create" do | |
26 | | |
27 | api = API.new | |
28 | $webprofile = WebProfile.new(WebProfileAttributes.merge( :token => api.token )) | |
29 | | |
30 | # generate a random number and append it to name | |
31 | suffix = Random.rand(1000000000).to_s | |
32 | $randname = $webprofile.name + suffix | |
33 | $webprofile.name = $randname | |
34 | | |
35 | # create webhook | |
36 | $webprofile.create | |
37 | expect($webprofile).to be_an_instance_of(WebProfile) | |
38 | expect($webprofile.name.to_s).to eq($randname) | |
39 | end | |
40 | | |
41 | it "List" do | |
42 | list = WebProfile.get_list | |
43 | expect(list.size).to be > 1 | |
44 | expect(list.first).to be_an_instance_of(WebProfileList) | |
45 | end | |
46 | | |
47 | it "Retrieve" do | |
48 | webprofile = WebProfile.find($webprofile.id) | |
49 | expect(webprofile).to be_an_instance_of(WebProfile) | |
50 | expect(webprofile.name.to_s).to eq($randname) | |
51 | end | |
52 | | |
53 | it "Update" do | |
54 | # append "-test" to web profile name | |
55 | webprofile = WebProfile.find($webprofile.id) | |
56 | webprofile.name += "-test" | |
57 | webprofile.update | |
58 | | |
59 | # check whether the name was updated | |
60 | webprofile = WebProfile.find($webprofile.id) | |
61 | expect(webprofile.name).to eq($randname + "-test") | |
62 | webprofile.name = $randname | |
63 | webprofile.update | |
64 | | |
65 | # revert updated profile name for next test run | |
66 | webprofile = WebProfile.find($webprofile.id) | |
67 | expect(webprofile.name).to eq($randname) | |
68 | end | |
69 | | |
70 | it "Partial update" do | |
71 | # retrieve web profile to perform partial update on | |
72 | webprofile = WebProfile.find($webprofile.id) | |
73 | | |
74 | # set up partial update | |
75 | h = {"op" => "replace", | |
76 | "path" => "/presentation/brand_name", | |
77 | "value" => "new_brand_name"} | |
78 | | |
79 | # do partial update by sending a patch request | |
80 | expect(webprofile.partial_update( [h] )).to be_truthy | |
81 | webprofile = WebProfile.find($webprofile.id) | |
82 | expect(webprofile.presentation.brand_name).to eq("new_brand_name") | |
83 | | |
84 | # restore original value for the next test run | |
85 | h = {"op" => "replace", | |
86 | "path" => "/presentation/brand_name", | |
87 | "value" => "brand_name"} | |
88 | expect(webprofile.partial_update( [h] )).to be_truthy | |
89 | expect(WebProfile.find($webprofile.id).presentation.brand_name).to eq("brand_name") | |
90 | end | |
91 | | |
92 | it "Delete" do | |
93 | # delete newly created web profile from above create test | |
94 | expect($webprofile.delete).to be_truthy | |
95 | | |
96 | # make sure it is not retrieved from the system | |
97 | begin | |
98 | webprofile = WebProfile.find($webprofile.id) | |
99 | expect(webprofile).to be_nil | |
100 | fail "WebProfile with ID=#{$webprofile.id} has not been deleted" | |
101 | rescue ResourceNotFound | |
102 | end | |
103 | end | |
104 | end | |
105 | end | |
106 | end | |
107 | | |
-------------------------------------------------------------------------------- | |
/spec/webhooks_examples_spec.rb: | |
-------------------------------------------------------------------------------- | |
1 | require "spec_helper" | |
2 | require "securerandom" | |
3 | | |
4 | describe "Webhooks" do | |
5 | | |
6 | webhookAttributes = { | |
7 | :url => "https://www.yeowza.com/paypal_webhook_"+SecureRandom.hex(8), | |
8 | :event_types => [ | |
9 | { | |
10 | :name => "PAYMENT.AUTHORIZATION.CREATED" | |
11 | }, | |
12 | { | |
13 | :name => "PAYMENT.AUTHORIZATION.VOIDED" | |
14 | } | |
15 | ] | |
16 | } | |
17 | | |
18 | describe "PayPal::SDK::Core::API::DataTypes::WebhookEvent" do | |
19 | describe "get event by id via .find" do | |
20 | it "exists" do | |
21 | expect(WebhookEvent).to respond_to(:find) | |
22 | end | |
23 | end | |
24 | end | |
25 | | |
26 | describe "Notifications", :integration => true do | |
27 | it "create webhook" do | |
28 | $webhook = PayPal::SDK::REST::Webhook.new(webhookAttributes) | |
29 | expect($webhook.create).to be_truthy | |
30 | end | |
31 | | |
32 | it "get webhook" do | |
33 | $result = PayPal::SDK::REST::Webhook.get($webhook.id) | |
34 | expect($result).to be_a PayPal::SDK::REST::Webhook | |
35 | expect($result.id).to eql $webhook.id | |
36 | end | |
37 | | |
38 | it "get all webhooks" do | |
39 | $webhooks_list = PayPal::SDK::REST::Webhook.all() | |
40 | expect($webhooks_list.webhooks.length).not_to be_nil | |
41 | end | |
42 | | |
43 | it "get subscribed webhook event types" do | |
44 | $webhook_event_types = PayPal::SDK::REST::Webhook.get_event_types($webhook.id) | |
45 | expect($webhook_event_types.event_types.length).to eql $webhook.event_types.length | |
46 | end | |
47 | | |
48 | it "delete webhook" do | |
49 | expect($webhook.delete).to be_truthy | |
50 | end | |
51 | end | |
52 | | |
53 | describe "Validation", :integration => true do | |
54 | | |
55 | transmission_id = "dfb3be50-fd74-11e4-8bf3-77339302725b" | |
56 | timestamp = "2015-05-18T15:45:13Z" | |
57 | webhook_id = "4JH86294D6297924G" | |
58 | actual_signature = "thy4/U002quzxFavHPwbfJGcc46E8rc5jzgyeafWm5mICTBdY/8rl7WJpn8JA0GKA+oDTPsSruqusw+XXg5RLAP7ip53Euh9Xu3UbUhQFX7UgwzE2FeYoY6lyRMiiiQLzy9BvHfIzNIVhPad4KnC339dr6y2l+mN8ALgI4GCdIh3/SoJO5wE64Bh/ueWtt8EVuvsvXfda2Le5a2TrOI9vLEzsm9GS79hAR/5oLexNz8UiZr045Mr5ObroH4w4oNfmkTaDk9Rj0G19uvISs5QzgmBpauKr7Nw++JI0pr/v5mFctQkoWJSGfBGzPRXawrvIIVHQ9Wer48GR2g9ZiApWg==" | |
59 | event_body = '{"id":"WH-0G2756385H040842W-5Y612302CV158622M","create_time":"2015-05-18T15:45:13Z","resource_type":"sale","event_type":"PAYMENT.SALE.COMPLETED","summary":"Payment completed for $ 20.0 USD","resource":{"id":"4EU7004268015634R","create_time":"2015-05-18T15:44:02Z","update_time":"2015-05-18T15:44:21Z","amount":{"total":"20.00","currency":"USD"},"payment_mode":"INSTANT_TRANSFER","state":"completed","protection_eligibility":"ELIGIBLE","protection_eligibility_type":"ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE","parent_payment":"PAY-86C81811X5228590KKVNARQQ","transaction_fee":{"value":"0.88","currency":"USD"},"links":[{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/payments/sale/4EU7004268015634R/refund","rel":"refund","method":"POST"},{"href":"https://api.sandbox.paypal.com/v1/payments/payment/PAY-86C81811X5228590KKVNARQQ","rel":"parent_payment","method":"GET"}]},"links":[{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M","rel":"self","method":"GET"},{"href":"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-0G2756385H040842W-5Y612302CV158622M/resend","rel":"resend","method":"POST"}]}' | |
60 | cert_url = "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-a5cafa77" | |
61 | | |
62 | it "verify common name" do | |
63 | cert = PayPal::SDK::REST::WebhookEvent.get_cert(cert_url) | |
64 | valid = PayPal::SDK::REST::WebhookEvent.verify_common_name(cert) | |
65 | expect(valid).to be_truthy | |
66 | end | |
67 | | |
68 | it "verify get expected signature" do | |
69 | expected_sig = PayPal::SDK::REST::WebhookEvent.get_expected_sig(transmission_id, timestamp, webhook_id, event_body) | |
70 | expect(expected_sig).eql?("dfb3be50-fd74-11e4-8bf3-77339302725b|2015-05-18T15:45:13Z|4JH86294D6297924G|2771810304") | |
71 | end | |
72 | | |
73 | it "verify expiry" do | |
74 | cert = PayPal::SDK::REST::WebhookEvent.get_cert(cert_url) | |
75 | valid = PayPal::SDK::REST::WebhookEvent.verify_expiration(cert) | |
76 | expect(valid).to be_truthy | |
77 | end | |
78 | | |
79 | it "verify cert chain" do | |
80 | cert = PayPal::SDK::REST::WebhookEvent.get_cert(cert_url) | |
81 | cert_store = PayPal::SDK::REST::WebhookEvent.get_cert_chain | |
82 | valid = PayPal::SDK::REST::WebhookEvent.verify_cert_chain(cert_store, cert) | |
83 | expect(valid).to be_truthy | |
84 | end | |
85 | | |
86 | it "verify" do | |
87 | valid = PayPal::SDK::REST::WebhookEvent.verify(transmission_id, timestamp, webhook_id, event_body, cert_url, actual_signature) | |
88 | expect(valid).to be_truthy | |
89 | end | |
90 | end | |
91 | | |
92 | | |
93 | end | |
94 | | |
-------------------------------------------------------------------------------- |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment