solidus_paypal_braintree 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +40 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/stale.yml +17 -0
  5. data/.gitignore +18 -0
  6. data/.rspec +2 -0
  7. data/.rubocop.yml +68 -0
  8. data/CHANGELOG.md +258 -0
  9. data/Gemfile +43 -0
  10. data/LICENSE +2 -2
  11. data/README.md +77 -23
  12. data/Rakefile +4 -25
  13. data/app/assets/javascripts/solidus_paypal_braintree/checkout.js +32 -3
  14. data/app/assets/javascripts/solidus_paypal_braintree/client.js +23 -4
  15. data/app/assets/javascripts/solidus_paypal_braintree/constants.js +19 -0
  16. data/app/assets/javascripts/solidus_paypal_braintree/frontend.js +1 -0
  17. data/app/assets/javascripts/solidus_paypal_braintree/hosted_form.js +15 -5
  18. data/app/assets/javascripts/solidus_paypal_braintree/paypal_button.js +47 -20
  19. data/app/assets/javascripts/solidus_paypal_braintree/paypal_messaging.js +22 -0
  20. data/app/decorators/controllers/solidus_paypal_braintree/admin_payments_controller_decorator.rb +11 -0
  21. data/app/decorators/controllers/solidus_paypal_braintree/checkout_controller_decorator.rb +11 -0
  22. data/app/decorators/controllers/solidus_paypal_braintree/client_tokens_controller.rb +41 -0
  23. data/app/decorators/controllers/solidus_paypal_braintree/orders_controller_decorator.rb +11 -0
  24. data/app/decorators/models/solidus_paypal_braintree/spree/store_decorator.rb +20 -0
  25. data/app/decorators/models/solidus_paypal_braintree/spree/user_decorator.rb +13 -0
  26. data/app/helpers/solidus_paypal_braintree/braintree_admin_helper.rb +23 -0
  27. data/app/helpers/solidus_paypal_braintree/braintree_checkout_helper.rb +46 -0
  28. data/app/models/application_record.rb +2 -0
  29. data/app/models/solidus_paypal_braintree/address.rb +30 -0
  30. data/app/models/solidus_paypal_braintree/configuration.rb +26 -3
  31. data/app/models/solidus_paypal_braintree/customer.rb +7 -3
  32. data/app/models/solidus_paypal_braintree/gateway.rb +52 -20
  33. data/app/models/solidus_paypal_braintree/response.rb +3 -2
  34. data/app/models/solidus_paypal_braintree/source.rb +21 -7
  35. data/app/models/solidus_paypal_braintree/transaction.rb +2 -0
  36. data/app/models/solidus_paypal_braintree/transaction_address.rb +30 -12
  37. data/app/models/solidus_paypal_braintree/transaction_import.rb +13 -9
  38. data/app/views/spree/api/payments/source_views/_paypal_braintree.json.jbuilder +3 -0
  39. data/app/views/spree/checkout/existing_payment/_paypal_braintree.html.erb +10 -0
  40. data/app/views/spree/shared/_apple_pay_button.html.erb +2 -2
  41. data/app/views/spree/shared/_braintree_errors.html.erb +11 -17
  42. data/app/views/spree/shared/_braintree_hosted_fields.html.erb +24 -9
  43. data/app/views/spree/shared/_paypal_braintree_head_scripts.html.erb +9 -6
  44. data/app/views/spree/shared/_paypal_cart_button.html.erb +16 -2
  45. data/app/views/spree/shared/_paypal_messaging.html.erb +13 -0
  46. data/bin/console +17 -0
  47. data/bin/rails +15 -0
  48. data/bin/setup +8 -0
  49. data/config/locales/en.yml +10 -0
  50. data/config/locales/it.yml +51 -8
  51. data/config/routes.rb +2 -0
  52. data/db/migrate/20160906201711_create_solidus_paypal_braintree_customers.rb +3 -1
  53. data/db/migrate/20161125172005_add_braintree_configuration_to_stores.rb +5 -7
  54. data/db/migrate/20170505193712_add_null_constraint_to_sources.rb +3 -1
  55. data/db/migrate/20190705115327_add_paypal_button_preferences_to_braintree_configurations.rb +5 -0
  56. data/db/migrate/20190911141712_add_3d_secure_to_braintree_configuration.rb +5 -0
  57. data/lib/controllers/backend/solidus_paypal_braintree/configurations_controller.rb +20 -5
  58. data/lib/controllers/frontend/solidus_paypal_braintree/checkouts_controller.rb +25 -21
  59. data/lib/controllers/frontend/solidus_paypal_braintree/transactions_controller.rb +55 -51
  60. data/lib/generators/solidus_paypal_braintree/install/install_generator.rb +7 -5
  61. data/lib/solidus_paypal_braintree.rb +4 -0
  62. data/lib/solidus_paypal_braintree/country_mapper.rb +4 -2
  63. data/lib/solidus_paypal_braintree/engine.rb +11 -11
  64. data/lib/solidus_paypal_braintree/factories.rb +8 -4
  65. data/lib/solidus_paypal_braintree/request_protection.rb +3 -0
  66. data/lib/solidus_paypal_braintree/version.rb +3 -1
  67. data/lib/views/backend/solidus_paypal_braintree/configurations/list.html.erb +30 -5
  68. data/lib/views/backend/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  69. data/lib/views/backend/spree/admin/payments/source_views/_paypal_braintree.html.erb +2 -2
  70. data/lib/views/backend_v1.2/spree/admin/payments/source_forms/_paypal_braintree.html.erb +2 -2
  71. data/lib/views/frontend/spree/checkout/payment/_paypal_braintree.html.erb +4 -2
  72. data/lib/views/frontend/spree/shared/_paypal_checkout_button.html.erb +30 -0
  73. data/solidus_paypal_braintree.gemspec +42 -0
  74. data/spec/controllers/solidus_paypal_braintree/checkouts_controller_spec.rb +99 -0
  75. data/spec/controllers/solidus_paypal_braintree/client_tokens_controller_spec.rb +55 -0
  76. data/spec/controllers/solidus_paypal_braintree/configurations_controller_spec.rb +73 -0
  77. data/spec/controllers/solidus_paypal_braintree/transactions_controller_spec.rb +183 -0
  78. data/spec/features/backend/configuration_spec.rb +23 -0
  79. data/spec/features/backend/new_payment_spec.rb +137 -0
  80. data/spec/features/frontend/braintree_credit_card_checkout_spec.rb +187 -0
  81. data/spec/features/frontend/paypal_checkout_spec.rb +166 -0
  82. data/spec/fixtures/cassettes/admin/invalid_credit_card.yml +63 -0
  83. data/spec/fixtures/cassettes/admin/resubmit_credit_card.yml +352 -0
  84. data/spec/fixtures/cassettes/admin/valid_credit_card.yml +412 -0
  85. data/spec/fixtures/cassettes/braintree/create_profile.yml +71 -0
  86. data/spec/fixtures/cassettes/braintree/generate_token.yml +63 -0
  87. data/spec/fixtures/cassettes/braintree/token.yml +63 -0
  88. data/spec/fixtures/cassettes/checkout/invalid_credit_card.yml +63 -0
  89. data/spec/fixtures/cassettes/checkout/resubmit_credit_card.yml +216 -0
  90. data/spec/fixtures/cassettes/checkout/update.yml +71 -0
  91. data/spec/fixtures/cassettes/checkout/valid_credit_card.yml +156 -0
  92. data/spec/fixtures/cassettes/gateway/authorize.yml +86 -0
  93. data/spec/fixtures/cassettes/gateway/authorize/credit_card/address.yml +86 -0
  94. data/spec/fixtures/cassettes/gateway/authorize/merchant_account/EUR.yml +154 -0
  95. data/spec/fixtures/cassettes/gateway/authorize/paypal/EUR.yml +90 -0
  96. data/spec/fixtures/cassettes/gateway/authorize/paypal/address.yml +90 -0
  97. data/spec/fixtures/cassettes/gateway/authorized_transaction.yml +73 -0
  98. data/spec/fixtures/cassettes/gateway/cancel/missing.yml +63 -0
  99. data/spec/fixtures/cassettes/gateway/cancel/refunds.yml +272 -0
  100. data/spec/fixtures/cassettes/gateway/cancel/void.yml +201 -0
  101. data/spec/fixtures/cassettes/gateway/capture.yml +141 -0
  102. data/spec/fixtures/cassettes/gateway/complete.yml +157 -0
  103. data/spec/fixtures/cassettes/gateway/credit.yml +208 -0
  104. data/spec/fixtures/cassettes/gateway/purchase.yml +87 -0
  105. data/spec/fixtures/cassettes/gateway/settled_transaction.yml +140 -0
  106. data/spec/fixtures/cassettes/gateway/void.yml +137 -0
  107. data/spec/fixtures/cassettes/source/card_type.yml +267 -0
  108. data/spec/fixtures/cassettes/source/last4.yml +267 -0
  109. data/spec/fixtures/cassettes/transaction/import/valid.yml +71 -0
  110. data/spec/fixtures/cassettes/transaction/import/valid/capture.yml +224 -0
  111. data/spec/fixtures/views/spree/orders/edit.html.erb +50 -0
  112. data/spec/helpers/solidus_paypal_braintree/braintree_admin_helper_spec.rb +17 -0
  113. data/spec/models/solidus_paypal_braintree/address_spec.rb +51 -0
  114. data/spec/models/solidus_paypal_braintree/avs_result_spec.rb +317 -0
  115. data/spec/models/solidus_paypal_braintree/gateway_spec.rb +692 -0
  116. data/spec/models/solidus_paypal_braintree/response_spec.rb +280 -0
  117. data/spec/models/solidus_paypal_braintree/source_spec.rb +360 -0
  118. data/spec/models/solidus_paypal_braintree/transaction_address_spec.rb +253 -0
  119. data/spec/models/solidus_paypal_braintree/transaction_import_spec.rb +283 -0
  120. data/spec/models/solidus_paypal_braintree/transaction_spec.rb +85 -0
  121. data/spec/models/spree/store_spec.rb +14 -0
  122. data/spec/requests/spree/api/orders_controller_spec.rb +36 -0
  123. data/spec/spec_helper.rb +26 -0
  124. data/spec/support/capybara.rb +7 -0
  125. data/spec/support/factories.rb +2 -0
  126. data/spec/support/gateway_helpers.rb +29 -0
  127. data/spec/support/order_ready_for_payment.rb +37 -0
  128. data/spec/support/vcr.rb +42 -0
  129. data/spec/support/views.rb +1 -0
  130. metadata +182 -194
  131. data/app/controllers/solidus_paypal_braintree/client_tokens_controller.rb +0 -22
  132. data/app/helpers/braintree_admin_helper.rb +0 -18
  133. data/app/models/spree/store_decorator.rb +0 -11
  134. data/app/views/spree/shared/_paypal_checkout_button.html.erb +0 -27
  135. data/config/initializers/braintree.rb +0 -1
@@ -0,0 +1,71 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers
6
+ body:
7
+ encoding: UTF-8
8
+ string: |
9
+ <?xml version="1.0" encoding="UTF-8"?>
10
+ <customer>
11
+ <payment-method-nonce>fake-valid-nonce</payment-method-nonce>
12
+ </customer>
13
+ headers:
14
+ Accept-Encoding:
15
+ - gzip
16
+ Accept:
17
+ - application/xml
18
+ User-Agent:
19
+ - Braintree Ruby Gem 2.98.0
20
+ X-Apiversion:
21
+ - '5'
22
+ Content-Type:
23
+ - application/xml
24
+ Authorization:
25
+ - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ==
26
+ response:
27
+ status:
28
+ code: 201
29
+ message: Created
30
+ headers:
31
+ Date:
32
+ - Fri, 13 Sep 2019 14:27:57 GMT
33
+ Content-Type:
34
+ - application/xml; charset=utf-8
35
+ Transfer-Encoding:
36
+ - chunked
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Xss-Protection:
40
+ - 1; mode=block
41
+ X-Content-Type-Options:
42
+ - nosniff
43
+ X-Authentication:
44
+ - basic_auth
45
+ X-User:
46
+ - 3v249hqtptsg744y
47
+ Vary:
48
+ - Accept-Encoding
49
+ Content-Encoding:
50
+ - gzip
51
+ Etag:
52
+ - W/"353759854f3f20558dfdfbeff3bd4668"
53
+ Cache-Control:
54
+ - max-age=0, private, must-revalidate
55
+ X-Runtime:
56
+ - '0.384787'
57
+ X-Request-Id:
58
+ - 02-1568384876.677-92.223.152.178-13026259
59
+ Content-Security-Policy:
60
+ - frame-ancestors 'self'
61
+ X-Broxyid:
62
+ - 02-1568384876.677-92.223.152.178-13026259
63
+ Strict-Transport-Security:
64
+ - max-age=31536000; includeSubDomains
65
+ body:
66
+ encoding: ASCII-8BIT
67
+ string: !binary |-
68
+ H4sIAG2ne10AA6xWXXPaOhB9z69g/O4YAy44Y5xp506405mmD01y27x0ZGuNFWTJlWQC/fVXMv7iw5RM+saePSutV2d3CW43GR2sQUjC2dxyr4fWAFjMMWHLufX4cGfPrNvwKogLqXgGIrwaDAKCQ8+fTob+eOgHjrYMqJ1xipiytT0VeOmPXqZRNk1WXjoOnK7XsBMipLIZymDACJ1bShRgOaWLoj5PzLMcse0RDhki9AjNU86Oz0jQ5gh7hUgSdeI+AUgBtpEaqG0OcwtrU5EMrHA0dH176Nvu+MGd3IymN970OXDagDK+yPHb4tuA3f1l0e2EAMVyl9KS8ghRU8Uf46ffeOGr5++fk/uH1fj+n4/el98fvcBpOdVHYKLsGAksqzSQEGhrGe++f4doLCKUagHYCGMBUtb47ul/zeo3r7BaG/a+LrpwS+5/+IrQ+/z1badFUHmlEgCqTryHBBsFDJs6n6VRHiNKVN9VApa6Z3qcOZdKv4FuJAj9iTucBk4X6n5OwZTYlrCNaJ6iUe+HHzLHlzBZod+AxH+gniv4+7qgOuWdvVCe4fToUuuVhZOhO5rNDIc1uBG1ba4Ln4hEOrPG7jJSTrGWaV8JjOLM+CKIho9sxfgr0ye1WEvblZInNpGyQCyGLv/Y2QS+v8Bv6MGWavSttITDx28dZoPWfAwRUe2X7MzWmaCC1olHnFNAzApNBQ21dLbkQujXsXXjFNR8QOfQQ08dApuciDIfO+NMpaE7Cpwj8AR7C0jo8o2Ge/QS3WMDPsw9QVRCFdXJpJ2q8eLOi/57KvDCpXiRrp/v/Jcfrv8S//vp5etCHgzgMjgFRFWqxdaRRAeraSRDS7ALQcNUqVzeOA6SEpS8jgQizIy2pa7OK9pea/05OdpmwNTPDFTK8U/Kl9xZa51f52x5C2xNBGeGMJeI4Yhv9Nhuzm9u1GI0HRUhtmpT20NrajmYJ6E7m7mBUxm1T6ciOO30Rw00BAE50gW559pX/W59HBdx+degjW+xmiaLSMaC5OYh9zdZ26qKr4CFcZ7Hs1Xg7KzaVzDyqyjnYVQ2hK4M0atVhCjxvOnETzw/cRM8nuEPcRK7Gkgm3ixKtOB6Q5uz/8J0WwPLuC3xqkeQjb8TIXQau3Y9WZFyVHS3+x5Qzs+gmqVw8s/B4aA9Wv9vGTznV//5xX9u7V+w9C9a+WcX/pl1f+Gyv3TVX7roL17zf1zyf2UDvbsFAqejtsYAbbZyCq/+BwAA//8DAGwXsMSxDAAA
69
+ http_version:
70
+ recorded_at: Fri, 13 Sep 2019 14:27:57 GMT
71
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,224 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/customers
6
+ body:
7
+ encoding: UTF-8
8
+ string: |
9
+ <?xml version="1.0" encoding="UTF-8"?>
10
+ <customer>
11
+ <payment-method-nonce>fake-valid-nonce</payment-method-nonce>
12
+ </customer>
13
+ headers:
14
+ Accept-Encoding:
15
+ - gzip
16
+ Accept:
17
+ - application/xml
18
+ User-Agent:
19
+ - Braintree Ruby Gem 2.98.0
20
+ X-Apiversion:
21
+ - '5'
22
+ Content-Type:
23
+ - application/xml
24
+ Authorization:
25
+ - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ==
26
+ response:
27
+ status:
28
+ code: 201
29
+ message: Created
30
+ headers:
31
+ Date:
32
+ - Fri, 13 Sep 2019 14:28:01 GMT
33
+ Content-Type:
34
+ - application/xml; charset=utf-8
35
+ Transfer-Encoding:
36
+ - chunked
37
+ X-Frame-Options:
38
+ - SAMEORIGIN
39
+ X-Xss-Protection:
40
+ - 1; mode=block
41
+ X-Content-Type-Options:
42
+ - nosniff
43
+ X-Authentication:
44
+ - basic_auth
45
+ X-User:
46
+ - 3v249hqtptsg744y
47
+ Vary:
48
+ - Accept-Encoding
49
+ Content-Encoding:
50
+ - gzip
51
+ Etag:
52
+ - W/"110ef4341df24590b6e3c609ec3ba459"
53
+ Cache-Control:
54
+ - max-age=0, private, must-revalidate
55
+ X-Runtime:
56
+ - '0.378468'
57
+ X-Request-Id:
58
+ - 02-1568384880.224-92.223.152.178-13026813
59
+ Content-Security-Policy:
60
+ - frame-ancestors 'self'
61
+ X-Broxyid:
62
+ - 02-1568384880.224-92.223.152.178-13026813
63
+ Strict-Transport-Security:
64
+ - max-age=31536000; includeSubDomains
65
+ body:
66
+ encoding: ASCII-8BIT
67
+ string: !binary |-
68
+ H4sIAHGne10AA6xWy3KjOhDd5ytc7AkPmxhSmOySqVlkc5PMJJspgRqjWEhcSTj2fP0IzMsPPE5ldu7Tp6Wmdbrb4d0mp5M1CEk4WxjOtW1MgCUcE7ZcGM9P96Zv3EVXYVJKxXMQ0dVkEhIc3cxtb+55wTS0tFWB2plkiClT23OBl4H7Po/zebryMk0aeit2SoRUJkM5TBihC0OJEgyrdlE05kl4XiC2PcIhR4QeoUXG2fEZKdocYR8QS6JO3CcAKcAmUhO1LWBhYG0qkoMRubYTmHZgOtMnZ3br+re2/RZafUAdXxb48nhHx/cBu/vropspAYrlLqUl5TGiVRVfpy+/8UOg3n5+Tx/fk4/Hp8R5fFr9Dq2e03wEJspMkMCySQMJgbZG5d337xCNxYRSLQATYSxAyhbfPb1M2jdvsFYb5r4uhnBPHn/4hjD6/O1tp0XQeKUSAKpNfIQEGwUMV3U+S6M8QZSosasELHXPjDgLLpV+A91IEAUzx56H1hAafk7JlNjWsIlokSF39MMPmdNLmKzUb0CSv1DPFfxrXdCc8qlesI96oT7DGtGl1iuLZrbj+n7FYR1eidqsroteiEQ6s84eMjJOsZbpWAkqxVXjiyAaPbMV4x9Mn9RjPW1XSp6aRMoSsQSG/GNnF/j1An+iB3tqpW+lJRw9/zdgdmjLxxAT1X/JzuydKSppm3jMOQXEjKiqYEWtnT25FPp1TN04Ja0+YHDooacNgU1BRJ2PmXOmsshxQ+sIPMHeAhK6fK69R6/RPTbgw9xTRCU0UYNM+qmaPNx78Y+XEj84FD9k67f74P3V8d3XvHDhGz8YwHVwBoiqTIttIIkB1tJIjpZgloJGmVKFvLUsJCUoeR0LRFg12pa6Oh9oe631ZxVomwNTv3JQGce/KF9ya611fl2w5R2wNRGcVYSFRAzHfKPHdnd+d6MWY9VRMWKrPrU9tKXWg3kWOb7vhFZjtD6diuB00B8t0BEEFEgX5FGXp/3d+zguk/qvQR/fYy1NlrFMBCmqh9zfZH2rKr4CFt3E7+uNXoQ7q/WVjPxf1vMwrhtCV4bo1SoilHrefBakXpA6KZ76+CZJE0cD6czz41QLbjS0O/sfTLc1sJybEq9GBNn5BxFCp7Fr15MVqUfFcLvvAfX8DJtZCif/HBwO2qP1/5nBc371n1/859b+BUv/opV/duGfWfcXLvtLV/2li/7iNf/XJf9PNtCXWyC0BmrrDNBmL6fo6g8AAAD//wMAfnRhhbEMAAA=
69
+ http_version:
70
+ recorded_at: Fri, 13 Sep 2019 14:28:01 GMT
71
+ - request:
72
+ method: post
73
+ uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions
74
+ body:
75
+ encoding: UTF-8
76
+ string: |
77
+ <?xml version="1.0" encoding="UTF-8"?>
78
+ <transaction>
79
+ <amount>15.00</amount>
80
+ <order-id>R999999999-ABCD1234</order-id>
81
+ <channel>Solidus</channel>
82
+ <options>
83
+ <store-in-vault-on-success type="boolean">true</store-in-vault-on-success>
84
+ </options>
85
+ <payment-method-token>6bjvxz</payment-method-token>
86
+ <shipping>
87
+ <first-name>John</first-name>
88
+ <last-name>Doe</last-name>
89
+ <street-address>10 Lovely Street Northwest</street-address>
90
+ <locality>Herndon</locality>
91
+ <postal-code>21088-0255</postal-code>
92
+ <region>AL</region>
93
+ <country-code-alpha2>US</country-code-alpha2>
94
+ </shipping>
95
+ <customer-id>670575593</customer-id>
96
+ <type>sale</type>
97
+ </transaction>
98
+ headers:
99
+ Accept-Encoding:
100
+ - gzip
101
+ Accept:
102
+ - application/xml
103
+ User-Agent:
104
+ - Braintree Ruby Gem 2.98.0
105
+ X-Apiversion:
106
+ - '5'
107
+ Content-Type:
108
+ - application/xml
109
+ Authorization:
110
+ - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ==
111
+ response:
112
+ status:
113
+ code: 201
114
+ message: Created
115
+ headers:
116
+ Date:
117
+ - Fri, 13 Sep 2019 14:28:02 GMT
118
+ Content-Type:
119
+ - application/xml; charset=utf-8
120
+ Transfer-Encoding:
121
+ - chunked
122
+ X-Frame-Options:
123
+ - SAMEORIGIN
124
+ X-Xss-Protection:
125
+ - 1; mode=block
126
+ X-Content-Type-Options:
127
+ - nosniff
128
+ X-Authentication:
129
+ - basic_auth
130
+ X-User:
131
+ - 3v249hqtptsg744y
132
+ Vary:
133
+ - Accept-Encoding
134
+ Content-Encoding:
135
+ - gzip
136
+ Etag:
137
+ - W/"f39e2777d3b86e6d871f5bc62e96f566"
138
+ Cache-Control:
139
+ - max-age=0, private, must-revalidate
140
+ X-Runtime:
141
+ - '0.365501'
142
+ X-Request-Id:
143
+ - 01-1568384881.777-92.223.152.178-12974361
144
+ Content-Security-Policy:
145
+ - frame-ancestors 'self'
146
+ X-Broxyid:
147
+ - 01-1568384881.777-92.223.152.178-12974361
148
+ Strict-Transport-Security:
149
+ - max-age=31536000; includeSubDomains
150
+ body:
151
+ encoding: ASCII-8BIT
152
+ string: !binary |-
153
+ H4sIAHKne10AA7RYS3PbNhC+51dodIf5kORIGZqpG0/sZhK3TZzU9iUDkqAImwRYAJQl//ou+BIpgrJzqE7i7oclsNjHt/Teb7N0siFCUs7Ops6JPZ0QFvKIsvXZ9PvNR7ScvvffeEpgJnGoAOW/mUw8GvkPp88LuVhGngUPWiYVVoX0caESLugzAU0t0lq1y4kvcUo8q/yrZWEhBLxth6jkCF5K/O/fLjxrKNZgnPGCKd9ZnNi2Z9VPWpERESaYKYTDUAsR7EcqkgU8VZ5l0pa7LQJk0E0YTc+mShRkalXWMdgSr4JyEQES7H9dNT90/vuHC8edzT2r1ZYnFwQrEiGsJtobZ9MIHhXNyNR3bWeF7BVyZjfO/J27fGe79+CTdkG5vsijX1u/X1B7XioOZ9IP1XWevrUXbxeL1ay5TxDHVEiFGM7I4VlBmeJxXcizHLOdQUMyTFOD/IkEkiqTrTzhzCSP8dYgXac8wKl2893sx3N0uVL3t5/i64fw6fomdK5vHp89a4/RrrC6vvACmqYQ+3u/yPD/dYhUghAIqygSREqTx7aKsEhf3Sgk5SFOqTKZF2QNOWtyK4fkTKv0Ws0d+61ndUXNtiHUxW78VJVar0A4zRPsvgo1ewnFCrgPGg4yzOpcDxwtLlhkSsRWI+vswELgXU8J/uzUNJORHAtFwR2SKJWSjEDO91eYjO+L30vmO2YDrMLEiElonnej0ZQGbUj6n3jCPKsjOIhL/4JD9d0//lpo+o49+cw3JN1NvpWKyTUXKnkiUulC34P+cuT6V0SwiMP2W0k3fv3zz55V/x2Gr+vYyyWy3cXipRj2vzMoMhEcAIqhnPB4cl4GGoYq0IWNhjc0qD20Kx8NdVhxbloyOxL3/nJuH6xpNGUWdOOiLuUopiSNZB2FG4mIEFwgcHvOmSSlkUF8aVzHYX20/wW67FFAY6J/+QegPyorRzHlMTab4cqhUEPXcHlPeAeaB1LlFzRHOaxyXi54CG8DPzR5iUt4aeniL/fr1QeImGOgvpX+VhxbE5Ex7chKBUnhn+eg2WiCNIYoXRtFVO8EnD+EDc664TTUFxTDxcMKiJeAiKFHCk1j4C0VGxlBKbxFFcEyqsiWZHlDPALOU4LZ1I9xKjW5awEN0YFToBCLpocq/kiYfxo8bLbQi6unShNQ5s9tx10udaFn3fI1953l0qmL17xJHTCKSjL5g0qdwu1zU39yKqrLzDhTie+4njUQDrA7ggWwKNfugUtp/d6aMCBdq0pKXFaFgXS/y4SnpbvNbZRmeE1QIVI/USqX7ywLS2gP8iQQmDKdOHXEn0CttnK8013jZ0YgWqOfKV9zawPnP8nZ+j1hGyo404AziVkU8C3Ql9Z+XTsFyTFwmmuuA7D6X2kSglOVwI51pXxk/AkKckdWgSISULXXV4+1qhBwcRCF6yLVdLODOtS0zUcza+ize2hHVu8X7wRPO4hGULtPygKKIbRR9rjH9KT9UstjpLWYhaT71qGycRePirAcHPY72MsOeWd4+XER/POjiC6dNLpMNvcfVw93ztK9y3KXXPED+qnzvJ4odOQaoqNg9N+C1KkKq+BqKRR74eN4sXg7X8WLVezE0WwZnYZx6IAgni+WQQyxPrq0srwhLONIRo8jqdzqa57cT+V63kMJhbgXux4ZaglEiSBgqI4Qnf8wpYAiy185ubT41sLRQbNy6cisWDlUggfa1PqtGRV1coHLZOMevdUOhZMcSifxcU5hS0N5dWDr8MStpPZSVYRTbKaERSBDQfNRytjRtyWz5MMoB3LAIwR0C2l/GsLoAAnbEsqIhS0fvEd3IgRNx8B3IyrL4DXqSGWFN/E2Uv7GBjwoWMO99Y0CPdQfC+BcIyHc6qtmBBM8I6n/jac0KiSEdC2oCLnY6BYaEzLW/PS7+ROqrnSgBV8EhZAVp4+IgkG3YcJ9lfmCOgOB+fV9zOAjySvhZKsPDf1AmLehhyMIVyCTJoNFGBqIPFzLyNn1yfNCc+3xWanqrzh6gAaq15uwddNDlAGRLKphTDf6qib91DXJs8ZAfSrWcUqfsXXZ2CjoZVslf3vJVkvyGFFPXDwevOX21rPMGuOa0pqEEpnCjVbKSUsUtdJgrd2BSqAKIqgIOlUIOC/mxkFUoljAnNEZB0bn1bH8aarFmL7ZZH909m3dGOyVM3Pm7tJ294c5wI1Rbh0D+CjlbqKkH5Il+yPy2Ec21+62qrHl3Y8Sbfc3h/nRbw5H1xq/a7QrhrOf+UvF+II9bYmuPiUBu07uZl/zwF3E+OH+9Pr2i/Pn5d89dtNrlP6b/wAAAP//AwAQnAh0bBYAAA==
154
+ http_version:
155
+ recorded_at: Fri, 13 Sep 2019 14:28:02 GMT
156
+ - request:
157
+ method: put
158
+ uri: https://api.sandbox.braintreegateway.com/merchants/7rdg92j7bm7fk5h3/transactions/j6z5s58d/submit_for_settlement
159
+ body:
160
+ encoding: UTF-8
161
+ string: |
162
+ <?xml version="1.0" encoding="UTF-8"?>
163
+ <transaction>
164
+ <amount>15.00</amount>
165
+ </transaction>
166
+ headers:
167
+ Accept-Encoding:
168
+ - gzip
169
+ Accept:
170
+ - application/xml
171
+ User-Agent:
172
+ - Braintree Ruby Gem 2.98.0
173
+ X-Apiversion:
174
+ - '5'
175
+ Content-Type:
176
+ - application/xml
177
+ Authorization:
178
+ - Basic bXdqa2t4d2NwMzJja2huZjphOTI5OGY0M2IzMGM2OTlkYjMwNzJjYzRhMDBmN2Y0OQ==
179
+ response:
180
+ status:
181
+ code: 200
182
+ message: OK
183
+ headers:
184
+ Date:
185
+ - Fri, 13 Sep 2019 14:28:03 GMT
186
+ Content-Type:
187
+ - application/xml; charset=utf-8
188
+ Transfer-Encoding:
189
+ - chunked
190
+ X-Frame-Options:
191
+ - SAMEORIGIN
192
+ X-Xss-Protection:
193
+ - 1; mode=block
194
+ X-Content-Type-Options:
195
+ - nosniff
196
+ X-Authentication:
197
+ - basic_auth
198
+ X-User:
199
+ - 3v249hqtptsg744y
200
+ Vary:
201
+ - Accept-Encoding
202
+ Content-Encoding:
203
+ - gzip
204
+ Etag:
205
+ - W/"77e50737669d11411f113d3437283723"
206
+ Cache-Control:
207
+ - max-age=0, private, must-revalidate
208
+ X-Runtime:
209
+ - '0.245194'
210
+ X-Request-Id:
211
+ - 01-1568384883.102-92.223.152.178-12974580
212
+ Content-Security-Policy:
213
+ - frame-ancestors 'self'
214
+ X-Broxyid:
215
+ - 01-1568384883.102-92.223.152.178-12974580
216
+ Strict-Transport-Security:
217
+ - max-age=31536000; includeSubDomains
218
+ body:
219
+ encoding: ASCII-8BIT
220
+ string: !binary |-
221
+ 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
222
+ http_version:
223
+ recorded_at: Fri, 13 Sep 2019 14:28:03 GMT
224
+ recorded_with: VCR 5.0.0
@@ -0,0 +1,50 @@
1
+ <!-- Modified cart view with PayPal button, from solidus_paypal_braintree -->
2
+
3
+ <% @body_id = 'cart' %>
4
+ <div data-hook="cart_container">
5
+ <h1><%= t('spree.shopping_cart') %></h1>
6
+
7
+ <% if @order.line_items.empty? %>
8
+
9
+ <div data-hook="empty_cart">
10
+ <p><%= t('spree.your_cart_is_empty') %></p>
11
+ <p><%= link_to t('spree.continue_shopping'), products_path, class: 'button continue' %></p>
12
+ </div>
13
+
14
+ <% else %>
15
+
16
+ <div data-hook="outside_cart_form">
17
+ <%= form_for @order, url: update_cart_path, html: {id: 'update-cart'} do |order_form| %>
18
+ <div data-hook="inside_cart_form">
19
+
20
+ <div data-hook="cart_items">
21
+ <%= render 'form', order_form: order_form %>
22
+ </div>
23
+
24
+ <div class="links columns sixteen alpha omega" data-hook="cart_buttons">
25
+ <%= order_form.text_field :coupon_code, size: 10, placeholder: t('spree.coupon_code') %>
26
+ <%= button_tag class: 'primary', id: 'update-button' do %>
27
+ <%= t('spree.update') %>
28
+ <% end %>
29
+ <%= button_tag class: 'button checkout primary', id: 'checkout-link', name: 'checkout' do %>
30
+ <%= t('spree.checkout') %>
31
+ <% end %>
32
+ </div>
33
+
34
+ </div>
35
+ <% end %>
36
+ </div>
37
+
38
+ <div id="empty-cart" data-hook>
39
+ <%= form_tag empty_cart_path, method: :put do %>
40
+ <p id="clear_cart_link" data-hook>
41
+ <%= submit_tag t('spree.empty_cart'), class: 'button gray' %>
42
+ <%= t('spree.or') %>
43
+ <%= link_to t('spree.continue_shopping'), products_path, class: 'continue button gray' %>
44
+ </p>
45
+ <% end %>
46
+ </div>
47
+
48
+ <%= render "spree/shared/paypal_cart_button" %>
49
+ <% end %>
50
+ </div>
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusPaypalBraintree::BraintreeAdminHelper do
4
+ describe '#braintree_transaction_link' do
5
+ subject { helper.braintree_transaction_link(payment) }
6
+
7
+ let(:payment_method) { create_gateway }
8
+ let(:payment) do
9
+ instance_double(Spree::Payment, payment_method: payment_method, response_code: 'abcde')
10
+ end
11
+ let(:merchant_id) { payment_method.preferences[:merchant_id] }
12
+
13
+ it 'generates a link to Braintree admin' do
14
+ expect(subject).to eq "<a title=\"Show payment on Braintree\" target=\"_blank\" rel=\"noopener\" href=\"https://sandbox.braintreegateway.com/merchants/#{merchant_id}/transactions/abcde\">abcde</a>" # rubocop:disable Layout/LineLength
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusPaypalBraintree::Address do
4
+ describe '#to_json' do
5
+ subject(:address_json) { JSON.parse(described_class.new(spree_address).to_json) }
6
+
7
+ let(:german_address) { create(:address, country_iso: 'DE', state: nil) } # Does not require states
8
+ let(:us_address) { create(:address, country_iso: 'US') } # Requires states
9
+ let(:spree_address) { us_address }
10
+
11
+ before do
12
+ create(:country, iso: 'DE', states_required: false)
13
+ create(:country, iso: 'US', states_required: true)
14
+ end
15
+
16
+ it 'has all the required keys' do
17
+ expect(address_json.keys).to contain_exactly(
18
+ 'line1',
19
+ 'line2',
20
+ 'city',
21
+ 'postalCode',
22
+ 'countryCode',
23
+ 'recipientName',
24
+ 'state',
25
+ 'phone'
26
+ )
27
+ end
28
+
29
+ context 'with a country that does not require state' do
30
+ let(:spree_address) { german_address }
31
+
32
+ it { is_expected.not_to have_key('state') }
33
+ end
34
+
35
+ context 'with states turned off globally' do
36
+ before do
37
+ allow(::Spree::Config).to receive(:address_requires_state).and_return(false)
38
+ end
39
+
40
+ context 'with a country that requires states' do
41
+ it { is_expected.not_to have_key('state') }
42
+ end
43
+
44
+ context 'with a country that does not require state' do
45
+ let(:spree_address) { german_address }
46
+
47
+ it { is_expected.not_to have_key('state') }
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,317 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe SolidusPaypalBraintree::AVSResult do
4
+ describe 'AVS response message' do
5
+ subject { described_class.build(transaction).to_hash['message'] }
6
+
7
+ context 'with avs_error_response_code' do
8
+ let(:transaction) do
9
+ instance_double('Braintree::Transaction',
10
+ avs_error_response_code: error_code,
11
+ avs_street_address_response_code: nil,
12
+ avs_postal_code_response_code: nil)
13
+ end
14
+
15
+ context 'when error code is S' do
16
+ let(:error_code) { 'S' }
17
+
18
+ it { is_expected.to eq 'U.S.-issuing bank does not support AVS.' }
19
+ end
20
+
21
+ context 'when error code is E' do
22
+ let(:error_code) { 'E' }
23
+
24
+ it { is_expected.to eq 'AVS data is invalid or AVS is not allowed for this card type.' }
25
+ end
26
+ end
27
+
28
+ context 'without avs_error_response_code' do
29
+ let(:transaction) do
30
+ instance_double('Braintree::Transaction',
31
+ avs_error_response_code: nil,
32
+ avs_street_address_response_code: codes.first,
33
+ avs_postal_code_response_code: codes.last)
34
+ end
35
+
36
+ context 'when street address result is M and postal code result is N' do
37
+ let(:codes) { %w(M N) }
38
+
39
+ it { is_expected.to eq 'Street address matches, but postal code does not match.' }
40
+
41
+ it {
42
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => 'N')
43
+ }
44
+ end
45
+
46
+ context 'when street address result is M and postal code result is U' do
47
+ let(:codes) { %w(M U) }
48
+
49
+ it { is_expected.to eq 'Street address matches, but postal code not verified.' }
50
+
51
+ it {
52
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => 'U')
53
+ }
54
+ end
55
+
56
+ context 'when street address result is M and postal code result is I' do
57
+ let(:codes) { %w(M I) }
58
+
59
+ it { is_expected.to eq 'Street address matches, but postal code not verified.' }
60
+
61
+ it {
62
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => 'I')
63
+ }
64
+ end
65
+
66
+ context 'when street address result is M and postal code result is A' do
67
+ let(:codes) { %w(M A) }
68
+
69
+ it { is_expected.to eq 'Street address matches, but postal code not verified.' }
70
+
71
+ it {
72
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => 'A')
73
+ }
74
+ end
75
+
76
+ context 'when street address result is N and postal code result is N' do
77
+ let(:codes) { %w(N N) }
78
+
79
+ it { is_expected.to eq 'Street address and postal code do not match.' }
80
+
81
+ it {
82
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'N', 'postal_match' => 'N')
83
+ }
84
+ end
85
+
86
+ context 'when street address result is N and postal code result is U' do
87
+ let(:codes) { %w(N U) }
88
+
89
+ it { is_expected.to eq 'Street address and postal code do not match.' }
90
+
91
+ it {
92
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'N', 'postal_match' => 'U')
93
+ }
94
+ end
95
+
96
+ context 'when street address result is N and postal code result is I' do
97
+ let(:codes) { %w(N I) }
98
+
99
+ it { is_expected.to eq 'Street address and postal code do not match.' }
100
+
101
+ it {
102
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'N', 'postal_match' => 'I')
103
+ }
104
+ end
105
+
106
+ context 'when street address result is N and postal code result is A' do
107
+ let(:codes) { %w(N A) }
108
+
109
+ it { is_expected.to eq 'Street address and postal code do not match.' }
110
+
111
+ it {
112
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'N', 'postal_match' => 'A')
113
+ }
114
+ end
115
+
116
+ context 'when street address result is I and postal code result is N' do
117
+ let(:codes) { %w(I N) }
118
+
119
+ it { is_expected.to eq 'Street address and postal code do not match.' }
120
+
121
+ it {
122
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'I', 'postal_match' => 'N')
123
+ }
124
+ end
125
+
126
+ context 'when street address result is A and postal code result is N' do
127
+ let(:codes) { %w(A N) }
128
+
129
+ it { is_expected.to eq 'Street address and postal code do not match.' }
130
+
131
+ it {
132
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'A', 'postal_match' => 'N')
133
+ }
134
+ end
135
+
136
+ context 'when street address result is U and postal code result is U' do
137
+ let(:codes) { %w(U U) }
138
+
139
+ it { is_expected.to eq 'Address not verified.' }
140
+
141
+ it {
142
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'U', 'postal_match' => 'U')
143
+ }
144
+ end
145
+
146
+ context 'when street address result is U and postal code result is I' do
147
+ let(:codes) { %w(U I) }
148
+
149
+ it { is_expected.to eq 'Address not verified.' }
150
+
151
+ it {
152
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'U', 'postal_match' => 'I')
153
+ }
154
+ end
155
+
156
+ context 'when street address result is U and postal code result is A' do
157
+ let(:codes) { %w(U A) }
158
+
159
+ it { is_expected.to eq 'Address not verified.' }
160
+
161
+ it {
162
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'U', 'postal_match' => 'A')
163
+ }
164
+ end
165
+
166
+ context 'when street address result is I and postal code result is U' do
167
+ let(:codes) { %w(I U) }
168
+
169
+ it { is_expected.to eq 'Address not verified.' }
170
+
171
+ it {
172
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'I', 'postal_match' => 'U')
173
+ }
174
+ end
175
+
176
+ context 'when street address result is I and postal code result is I' do
177
+ let(:codes) { %w(I I) }
178
+
179
+ it { is_expected.to eq 'Address not verified.' }
180
+
181
+ it {
182
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'I', 'postal_match' => 'I')
183
+ }
184
+ end
185
+
186
+ context 'when street address result is I and postal code result is A' do
187
+ let(:codes) { %w(I A) }
188
+
189
+ it { is_expected.to eq 'Address not verified.' }
190
+
191
+ it {
192
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'I', 'postal_match' => 'A')
193
+ }
194
+ end
195
+
196
+ context 'when street address result is A and postal code result is U' do
197
+ let(:codes) { %w(A U) }
198
+
199
+ it { is_expected.to eq 'Address not verified.' }
200
+
201
+ it {
202
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'A', 'postal_match' => 'U')
203
+ }
204
+ end
205
+
206
+ context 'when street address result is A and postal code result is I' do
207
+ let(:codes) { %w(A I) }
208
+
209
+ it { is_expected.to eq 'Address not verified.' }
210
+
211
+ it {
212
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'A', 'postal_match' => 'I')
213
+ }
214
+ end
215
+
216
+ context 'when street address result is A and postal code result is A' do
217
+ let(:codes) { %w(A A) }
218
+
219
+ it { is_expected.to eq 'Address not verified.' }
220
+
221
+ it {
222
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'A', 'postal_match' => 'A')
223
+ }
224
+ end
225
+
226
+ context 'when street address result is M and postal code result is M' do
227
+ let(:codes) { %w(M M) }
228
+
229
+ it { is_expected.to eq 'Street address and postal code match.' }
230
+
231
+ it {
232
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => 'M')
233
+ }
234
+ end
235
+
236
+ context 'when street address result is U and postal code result is N' do
237
+ let(:codes) { %w(U N) }
238
+
239
+ it { is_expected.to eq "Street address and postal code do not match. For American Express: Card member's name, street address and postal code do not match." } # rubocop:disable Layout/LineLength
240
+
241
+ it {
242
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'U', 'postal_match' => 'N')
243
+ }
244
+ end
245
+
246
+ context 'when street address result is U and postal code result is M' do
247
+ let(:codes) { %w(U M) }
248
+
249
+ it { is_expected.to eq 'Postal code matches, but street address not verified.' }
250
+
251
+ it {
252
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'U', 'postal_match' => 'M')
253
+ }
254
+ end
255
+
256
+ context 'when street address result is I and postal code result is M' do
257
+ let(:codes) { %w(I M) }
258
+
259
+ it { is_expected.to eq 'Postal code matches, but street address not verified.' }
260
+
261
+ it {
262
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'I', 'postal_match' => 'M')
263
+ }
264
+ end
265
+
266
+ context 'when street address result is A and postal code result is M' do
267
+ let(:codes) { %w(A M) }
268
+
269
+ it { is_expected.to eq 'Postal code matches, but street address not verified.' }
270
+
271
+ it {
272
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'A', 'postal_match' => 'M')
273
+ }
274
+ end
275
+
276
+ context 'when street address result is N and postal code result is M' do
277
+ let(:codes) { %w(N M) }
278
+
279
+ it { is_expected.to eq 'Street address does not match, but 5-digit postal code matches.' }
280
+
281
+ it {
282
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'N', 'postal_match' => 'M')
283
+ }
284
+ end
285
+
286
+ context 'when street address response code is nil' do
287
+ let(:codes) { [nil, 'M'] }
288
+
289
+ it { is_expected.to be_nil }
290
+
291
+ it {
292
+ expect(described_class.build(transaction).to_hash).to include('street_match' => nil, 'postal_match' => 'M')
293
+ }
294
+ end
295
+
296
+ context 'when postal code response code is nil' do
297
+ let(:codes) { ['M', nil] }
298
+
299
+ it { is_expected.to be_nil }
300
+
301
+ it {
302
+ expect(described_class.build(transaction).to_hash).to include('street_match' => 'M', 'postal_match' => nil)
303
+ }
304
+ end
305
+
306
+ context 'when postal code and street address response code is nil' do
307
+ let(:codes) { [nil, nil] }
308
+
309
+ it { is_expected.to be_nil }
310
+
311
+ it {
312
+ expect(described_class.build(transaction).to_hash).to include('street_match' => nil, 'postal_match' => nil)
313
+ }
314
+ end
315
+ end
316
+ end
317
+ end