Przeglądaj źródła

#1409 Stripe PHP SDK updated from 4.4.0 to 6.15.0

Alex Trofimov 8 lat temu
rodzic
commit
be35f8350b
100 zmienionych plików z 4486 dodań i 2731 usunięć
  1. 3 0
      plugins/stripe/.gitignore
  2. 27 4
      plugins/stripe/.travis.yml
  3. 315 205
      plugins/stripe/CHANGELOG.md
  4. 100 12
      plugins/stripe/README.md
  5. 1 1
      plugins/stripe/VERSION
  6. 1 1
      plugins/stripe/build.php
  7. 1 1
      plugins/stripe/composer.json
  8. 421 694
      plugins/stripe/data/ca-certificates.crt
  9. BIN
      plugins/stripe/data/test.png
  10. 51 3
      plugins/stripe/init.php
  11. 144 35
      plugins/stripe/lib/Account.php
  12. 60 1
      plugins/stripe/lib/AlipayAccount.php
  13. 34 0
      plugins/stripe/lib/ApiOperations/All.php
  14. 28 0
      plugins/stripe/lib/ApiOperations/Create.php
  15. 27 0
      plugins/stripe/lib/ApiOperations/Delete.php
  16. 115 0
      plugins/stripe/lib/ApiOperations/NestedResource.php
  17. 60 0
      plugins/stripe/lib/ApiOperations/Request.php
  18. 27 0
      plugins/stripe/lib/ApiOperations/Retrieve.php
  19. 46 0
      plugins/stripe/lib/ApiOperations/Update.php
  20. 212 40
      plugins/stripe/lib/ApiRequestor.php
  21. 40 131
      plugins/stripe/lib/ApiResource.php
  22. 8 45
      plugins/stripe/lib/ApplePayDomain.php
  23. 51 22
      plugins/stripe/lib/ApplicationFee.php
  24. 16 0
      plugins/stripe/lib/ApplicationFeeRefund.php
  25. 0 31
      plugins/stripe/lib/AttachedObject.php
  26. 7 3
      plugins/stripe/lib/Balance.php
  27. 7 23
      plugins/stripe/lib/BalanceTransaction.php
  28. 75 1
      plugins/stripe/lib/BankAccount.php
  29. 18 54
      plugins/stripe/lib/BitcoinReceiver.php
  30. 1 0
      plugins/stripe/lib/BitcoinTransaction.php
  31. 93 1
      plugins/stripe/lib/Card.php
  32. 73 66
      plugins/stripe/lib/Charge.php
  33. 19 7
      plugins/stripe/lib/Collection.php
  34. 11 30
      plugins/stripe/lib/CountrySpec.php
  35. 21 63
      plugins/stripe/lib/Coupon.php
  36. 89 76
      plugins/stripe/lib/Customer.php
  37. 21 0
      plugins/stripe/lib/Discount.php
  38. 32 38
      plugins/stripe/lib/Dispute.php
  39. 41 0
      plugins/stripe/lib/EphemeralKey.php
  40. 9 0
      plugins/stripe/lib/Error/Base.php
  41. 6 7
      plugins/stripe/lib/Error/Card.php
  42. 7 0
      plugins/stripe/lib/Error/Idempotency.php
  43. 10 0
      plugins/stripe/lib/Error/OAuth/InvalidClient.php
  44. 13 0
      plugins/stripe/lib/Error/OAuth/InvalidGrant.php
  45. 11 0
      plugins/stripe/lib/Error/OAuth/InvalidRequest.php
  46. 10 0
      plugins/stripe/lib/Error/OAuth/InvalidScope.php
  47. 23 0
      plugins/stripe/lib/Error/OAuth/OAuthBase.php
  48. 11 0
      plugins/stripe/lib/Error/OAuth/UnsupportedGrantType.php
  49. 11 0
      plugins/stripe/lib/Error/OAuth/UnsupportedResponseType.php
  50. 20 0
      plugins/stripe/lib/Error/SignatureVerification.php
  51. 96 20
      plugins/stripe/lib/Event.php
  52. 17 0
      plugins/stripe/lib/ExchangeRate.php
  53. 0 89
      plugins/stripe/lib/ExternalAccount.php
  54. 30 0
      plugins/stripe/lib/FileLink.php
  55. 10 34
      plugins/stripe/lib/FileUpload.php
  56. 5 2
      plugins/stripe/lib/HttpClient/ClientInterface.php
  57. 128 69
      plugins/stripe/lib/HttpClient/CurlClient.php
  58. 49 54
      plugins/stripe/lib/Invoice.php
  59. 25 63
      plugins/stripe/lib/InvoiceItem.php
  60. 30 0
      plugins/stripe/lib/InvoiceLineItem.php
  61. 25 0
      plugins/stripe/lib/IssuerFraudRecord.php
  62. 69 0
      plugins/stripe/lib/Issuing/Authorization.php
  63. 51 0
      plugins/stripe/lib/Issuing/Card.php
  64. 21 0
      plugins/stripe/lib/Issuing/CardDetails.php
  65. 30 0
      plugins/stripe/lib/Issuing/Cardholder.php
  66. 30 0
      plugins/stripe/lib/Issuing/Dispute.php
  67. 33 0
      plugins/stripe/lib/Issuing/Transaction.php
  68. 0 18
      plugins/stripe/lib/JsonSerializable.php
  69. 18 0
      plugins/stripe/lib/LoginLink.php
  70. 93 0
      plugins/stripe/lib/OAuth.php
  71. 29 52
      plugins/stripe/lib/Order.php
  72. 22 0
      plugins/stripe/lib/OrderItem.php
  73. 14 20
      plugins/stripe/lib/OrderReturn.php
  74. 90 0
      plugins/stripe/lib/PaymentIntent.php
  75. 69 0
      plugins/stripe/lib/Payout.php
  76. 25 75
      plugins/stripe/lib/Plan.php
  77. 26 63
      plugins/stripe/lib/Product.php
  78. 22 66
      plugins/stripe/lib/Recipient.php
  79. 39 0
      plugins/stripe/lib/RecipientTransfer.php
  80. 9 55
      plugins/stripe/lib/Refund.php
  81. 21 63
      plugins/stripe/lib/SKU.php
  82. 33 0
      plugins/stripe/lib/Sigma/ScheduledQueryRun.php
  83. 3 1
      plugins/stripe/lib/SingletonApiResource.php
  84. 65 33
      plugins/stripe/lib/Source.php
  85. 23 0
      plugins/stripe/lib/SourceTransaction.php
  86. 122 6
      plugins/stripe/lib/Stripe.php
  87. 344 115
      plugins/stripe/lib/StripeObject.php
  88. 47 55
      plugins/stripe/lib/Subscription.php
  89. 14 74
      plugins/stripe/lib/SubscriptionItem.php
  90. 6 22
      plugins/stripe/lib/ThreeDSecure.php
  91. 7 23
      plugins/stripe/lib/Token.php
  92. 49 0
      plugins/stripe/lib/Topup.php
  93. 53 56
      plugins/stripe/lib/Transfer.php
  94. 8 1
      plugins/stripe/lib/TransferReversal.php
  95. 44 0
      plugins/stripe/lib/UsageRecord.php
  96. 3 3
      plugins/stripe/lib/Util/AutoPagingIterator.php
  97. 18 0
      plugins/stripe/lib/Util/DefaultLogger.php
  98. 36 0
      plugins/stripe/lib/Util/LoggerInterface.php
  99. 34 0
      plugins/stripe/lib/Util/RandomGenerator.php
  100. 24 4
      plugins/stripe/lib/Util/RequestOptions.php

+ 3 - 0
plugins/stripe/.gitignore

@@ -12,3 +12,6 @@ composer.lock
 
 # Ignore PHPUnit coverage file
 clover.xml
+
+# Ignore IDE's configuration files
+.idea

+ 27 - 4
plugins/stripe/.travis.yml

@@ -1,17 +1,40 @@
+sudo: false
+
 language: php
 
 php:
-  - 5.3
   - 5.4
   - 5.5
   - 5.6
   - 7.0
+  - 7.1
+  - 7.2
   - hhvm
 
 env:
-  - AUTOLOAD=1
-  - AUTOLOAD=0
+  global:
+    - STRIPE_MOCK_VERSION=0.25.0
+  matrix:
+    - AUTOLOAD=1
+    - AUTOLOAD=0
+
+cache:
+  directories:
+    - $HOME/.composer/cache/files
+    - stripe-mock
+
+before_install:
+  # Unpack and start stripe-mock so that the test suite can talk to it
+  - |
+    if [ ! -d "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}" ]; then
+      mkdir -p stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/
+      curl -L "https://github.com/stripe/stripe-mock/releases/download/v${STRIPE_MOCK_VERSION}/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -o "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz"
+      tar -zxf "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}_linux_amd64.tar.gz" -C "stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/"
+    fi
+  - |
+    stripe-mock/stripe-mock_${STRIPE_MOCK_VERSION}/stripe-mock > /dev/null &
+    STRIPE_MOCK_PID=$!
 
 script: ./build.php ${AUTOLOAD}
+
 after_script: ./vendor/bin/coveralls -v
-sudo: false

+ 315 - 205
plugins/stripe/CHANGELOG.md

@@ -1,213 +1,381 @@
-### 4.4.0 2017-01-18
+# Changelog
 
-* Add support for updating sources
+## 6.15.0 - 2018-08-03
+* [#504](https://github.com/stripe/stripe-php/pull/504) Add cancel support for topups
 
-### 4.3.0 2016-11-30
+## 6.14.0 - 2018-08-02
+* [#505](https://github.com/stripe/stripe-php/pull/505) Add support for file links
 
-* Add support for verifying sources
+## 6.13.0 - 2018-07-31
+* [#502](https://github.com/stripe/stripe-php/pull/502) Add `isDeleted()` method to `\Stripe\StripeObject`
 
-### 4.2.0 2016-11-21
+## 6.12.0 - 2018-07-28
+* [#501](https://github.com/stripe/stripe-php/pull/501) Add support for scheduled query runs (`\Stripe\Sigma\ScheduledQueryRun`) for Sigma
 
-* Add retrieve method for 3-D Secure resources
+## 6.11.0 - 2018-07-26
+* [#500](https://github.com/stripe/stripe-php/pull/500) Add support for Stripe Issuing
 
-### 4.1.1 2016-10-21
+## 6.10.4 - 2018-07-19
+* [#498](https://github.com/stripe/stripe-php/pull/498) Internal improvements to the `\Stripe\ApiResource.classUrl()` method
 
-* Add docblock with model properties for `Plan`
+## 6.10.3 - 2018-07-16
+* [#497](https://github.com/stripe/stripe-php/pull/497) Use HTTP/2 only for HTTPS requests
 
-### 4.1.0 2016-10-18
+## 6.10.2 - 2018-07-11
+* [#494](https://github.com/stripe/stripe-php/pull/494) Enable HTTP/2 support
 
-* Support for 403 status codes (permission denied)
+## 6.10.1 - 2018-07-10
+* [#493](https://github.com/stripe/stripe-php/pull/493) Add PHPDoc for `auto_advance` on `\Stripe\Invoice`
+
+## 6.10.0 - 2018-06-28
+* [#488](https://github.com/stripe/stripe-php/pull/488) Add support for `$appPartnerId` to `Stripe::setAppInfo()`
+
+## 6.9.0 - 2018-06-28
+* [#487](https://github.com/stripe/stripe-php/pull/487) Add support for payment intents
+
+## 6.8.2 - 2018-06-24
+* [#486](https://github.com/stripe/stripe-php/pull/486) Make `Account.deauthorize()` return the `StripeObject` from the API
+
+## 6.8.1 - 2018-06-13
+* [#472](https://github.com/stripe/stripe-php/pull/472) Added phpDoc for `ApiRequestor` and others, especially regarding thrown errors
+
+## 6.8.0 - 2018-06-13
+* [#481](https://github.com/stripe/stripe-php/pull/481) Add new `\Stripe\Discount` and `\Stripe\OrderItem` classes, add more PHPDoc describing object attributes
+
+## 6.7.4 - 2018-05-29
+* [#480](https://github.com/stripe/stripe-php/pull/480) PHPDoc changes for API version 2018-05-21 and the addition of the new `CHARGE_EXPIRED` event type
+
+## 6.7.3 - 2018-05-28
+* [#479](https://github.com/stripe/stripe-php/pull/479) Fix unnecessary traits on `\Stripe\InvoiceLineItem`
+
+## 6.7.2 - 2018-05-28
+* [#471](https://github.com/stripe/stripe-php/pull/471) Add `OBJECT_NAME` constant to all API resource classes, add `\Stripe\InvoiceLineItem` class
+
+## 6.7.1 - 2018-05-13
+* [#468](https://github.com/stripe/stripe-php/pull/468) Update fields in PHP docs for accuracy
+
+## 6.7.0 - 2018-05-09
+* [#466](https://github.com/stripe/stripe-php/pull/466) Add support for issuer fraud records
+
+## 6.6.0 - 2018-04-11
+* [#460](https://github.com/stripe/stripe-php/pull/460) Add support for flexible billing primitives
+
+## 6.5.0 - 2018-04-05
+* [#461](https://github.com/stripe/stripe-php/pull/461) Don't zero keys on non-`metadata` subobjects
+
+## 6.4.2 - 2018-03-17
+* [#458](https://github.com/stripe/stripe-php/pull/458) Add PHPDoc for `account` on `\Stripe\Event`
+
+## 6.4.1 - 2018-03-02
+* [#455](https://github.com/stripe/stripe-php/pull/455) Fix namespaces in PHPDoc
+* [#456](https://github.com/stripe/stripe-php/pull/456) Fix namespaces for some exceptions
+
+## 6.4.0 - 2018-02-28
+* [#453](https://github.com/stripe/stripe-php/pull/453) Add constants for `reason` (`REASON_*`) and `status` (`STATUS_*`) on `\Stripe\Dispute`
+
+## 6.3.2 - 2018-02-27
+* [#452](https://github.com/stripe/stripe-php/pull/452) Add PHPDoc for `amount_paid` and `amount_remaining` on `\Stripe\Invoice`
+
+## 6.3.1 - 2018-02-26
+* [#443](https://github.com/stripe/stripe-php/pull/443) Add event types as constants to `\Stripe\Event` class
+
+## 6.3.0 - 2018-02-23
+* [#450](https://github.com/stripe/stripe-php/pull/450) Add support for `code` attribute on all Stripe exceptions
+
+## 6.2.0 - 2018-02-21
+* [#440](https://github.com/stripe/stripe-php/pull/440) Add support for topups
+* [#442](https://github.com/stripe/stripe-php/pull/442) Fix PHPDoc for `\Stripe\Error\SignatureVerification`
+
+## 6.1.0 - 2018-02-12
+* [#435](https://github.com/stripe/stripe-php/pull/435) Fix header persistence on `Collection` objects
+* [#436](https://github.com/stripe/stripe-php/pull/436) Introduce new `Idempotency` error class
+
+## 6.0.0 - 2018-02-07
+Major version release. List of backwards incompatible changes to watch out for:
++ The minimum PHP version is now 5.4.0. If you're using PHP 5.3 or older, consider upgrading to a more recent version.
+* `\Stripe\AttachedObject` no longer exists. Attributes that used to be instances of `\Stripe\AttachedObject` (such as `metadata`) are now instances of `\Stripe\StripeObject`.
++ Attributes that used to be PHP arrays (such as `legal_entity->additional_owners` on `\Stripe\Account` instances) are now instances of `\Stripe\StripeObject`, except when they are empty. `\Stripe\StripeObject` has array semantics so this should not be an issue unless you are actively checking types.
+* `\Stripe\Collection` now derives from `\Stripe\StripeObject` rather than from `\Stripe\ApiResource`.
+
+Pull requests included in this release:
+* [#410](https://github.com/stripe/stripe-php/pull/410) Drop support for PHP 5.3
+* [#411](https://github.com/stripe/stripe-php/pull/411) Use traits for common API operations
+* [#414](https://github.com/stripe/stripe-php/pull/414) Use short array syntax
+* [#404](https://github.com/stripe/stripe-php/pull/404) Fix serialization logic
+* [#417](https://github.com/stripe/stripe-php/pull/417) Remove `ExternalAccount` class
+* [#418](https://github.com/stripe/stripe-php/pull/418) Increase test coverage
+* [#421](https://github.com/stripe/stripe-php/pull/421) Update CA bundle and add script for future updates
+* [#422](https://github.com/stripe/stripe-php/pull/422) Use vendored CA bundle for all requests
+* [#428](https://github.com/stripe/stripe-php/pull/428) Support for automatic request retries
+
+## 5.9.2 - 2018-02-07
+* [#431](https://github.com/stripe/stripe-php/pull/431) Update PHPDoc @property tags for latest API version
+
+## 5.9.1 - 2018-02-06
+* [#427](https://github.com/stripe/stripe-php/pull/427) Add and update PHPDoc @property tags on all API resources
+
+## 5.9.0 - 2018-01-17
+* [#421](https://github.com/stripe/stripe-php/pull/421) Updated bundled CA certificates
+* [#423](https://github.com/stripe/stripe-php/pull/423) Escape unsanitized input in OAuth example
+
+## 5.8.0 - 2017-12-20
+* [#403](https://github.com/stripe/stripe-php/pull/403) Add `__debugInfo()` magic method to `StripeObject`
+
+## 5.7.0 - 2017-11-28
+* [#390](https://github.com/stripe/stripe-php/pull/390) Remove some unsupported API methods
+* [#391](https://github.com/stripe/stripe-php/pull/391) Alphabetize the list of API resources in `Util::convertToStripeObject()` and add missing resources
+* [#393](https://github.com/stripe/stripe-php/pull/393) Fix expiry date update for card sources
+
+## 5.6.0 - 2017-10-31
+* [#386](https://github.com/stripe/stripe-php/pull/386) Support for exchange rates APIs
+
+## 5.5.1 - 2017-10-30
+* [#387](https://github.com/stripe/stripe-php/pull/387) Allow `personal_address_kana` and `personal_address_kanji` to be updated on an account
+
+## 5.5.0 - 2017-10-27
+* [#385](https://github.com/stripe/stripe-php/pull/385) Support for listing source transactions
+
+## 5.4.0 - 2017-10-24
+* [#383](https://github.com/stripe/stripe-php/pull/383) Add static methods to manipulate resources from parent
+    * `Account` gains methods for external accounts and login links (e.g. `createExternalAccount`, `createLoginLink`)
+    * `ApplicationFee` gains methods for refunds
+    * `Customer` gains methods for sources
+    * `Transfer` gains methods for reversals
+
+## 5.3.0 - 2017-10-11
+* [#378](https://github.com/stripe/stripe-php/pull/378) Rename source `delete` to `detach` (and deprecate the former)
+
+## 5.2.3 - 2017-09-27
+* Add PHPDoc for `Card`
 
-### 4.0.1 2016-10-17
+## 5.2.2 - 2017-09-20
+* Fix deserialization mapping of `FileUpload` objects
 
+## 5.2.1 - 2017-09-14
+* Serialized `shipping` nested attribute
+
+## 5.2.0 - 2017-08-29
+* Add support for `InvalidClient` OAuth error
+
+## 5.1.3 - 2017-08-14
+* Allow `address_kana` and `address_kanji` to be updated for custom accounts
+
+## 5.1.2 - 2017-08-01
+* Fix documented return type of `autoPagingIterator()` (was missing namespace)
+
+## 5.1.1 - 2017-07-03
+* Fix order returns to use the right URL `/v1/order_returns`
+
+## 5.1.0 - 2017-06-30
+* Add support for OAuth
+
+## 5.0.0 - 2017-06-27
+* `pay` on invoice now takes params as well as opts
+
+## 4.13.0 - 2017-06-19
+* Add support for ephemeral keys
+
+## 4.12.0 - 2017-06-05
+* Clients can implement `getUserAgentInfo()` to add additional user agent information
+
+## 4.11.0 - 2017-06-05
+* Implement `Countable` for `AttachedObject` (`metadata` and `additional_owners`)
+
+## 4.10.0 - 2017-05-25
+* Add support for login links
+
+## 4.9.1 - 2017-05-10
+* Fix docs to include arrays on `$id` parameter for retrieve methods
+
+## 4.9.0 - 2017-04-28
+* Support for checking webhook signatures
+
+## 4.8.1 - 2017-04-24
+* Allow nested field `payout_schedule` to be updated
+
+## 4.8.0 - 2017-04-20
+* Add `\Stripe\Stripe::setLogger()` to support an external PSR-3 compatible logger
+
+## 4.7.0 - 2017-04-10
+* Add support for payouts and recipient transfers
+
+## 4.6.0 - 2017-04-06
+* Please see 4.7.0 instead (no-op release)	
+
+## 4.5.1 - 2017-03-22
+* Remove hard dependency on cURL
+
+## 4.5.0 - 2017-03-20
+* Support for detaching sources from customers
+
+## 4.4.2 - 2017-02-27
+* Correct handling of `owner` parameter when updating sources
+
+## 4.4.1 - 2017-02-24
+* Correct the error check on a bad JSON decoding
+
+## 4.4.0 - 2017-01-18
+* Add support for updating sources
+
+## 4.3.0 - 2016-11-30
+* Add support for verifying sources
+
+## 4.2.0 - 2016-11-21
+* Add retrieve method for 3-D Secure resources
+
+## 4.1.1 - 2016-10-21
+* Add docblock with model properties for `Plan`
+
+## 4.1.0 - 2016-10-18
+* Support for 403 status codes (permission denied)
+
+## 4.0.1 - 2016-10-17
 * Fix transfer reversal materialization
 * Fixes for some property definitions in docblocks
 
-### 4.0.0 2016-09-28
-
+## 4.0.0 - 2016-09-28
 * Support for subscription items
 * Drop attempt to force TLS 1.2: please note that this could be breaking if you're using old OS distributions or packages and upgraded recently (so please make sure to test your integration!)
 
-### 3.23.0 2016-09-15
-
+## 3.23.0 - 2016-09-15
 * Add support for Apple Pay domains
 
-### 3.22.0 2016-09-13
-
+## 3.22.0 - 2016-09-13
 * Add `Stripe::setAppInfo` to allow plugins to register user agent information
 
-### 3.21.0 2016-08-25
-
+## 3.21.0 - 2016-08-25
 * Add `Source` model for generic payment sources
 
-### 3.20.0 2016-08-08
-
+## 3.20.0 - 2016-08-08
 * Add `getDeclineCode` to card errors
 
-### 3.19.0 2016-07-29
-
+## 3.19.0 - 2016-07-29
 * Opt requests directly into TLS 1.2 where OpenSSL >= 1.0.1 (see #277 for context)
 
-### 3.18.0 2016-07-28
-
+## 3.18.0 - 2016-07-28
 * Add new `STATUS_` constants for subscriptions
 
-### 3.17.1 2016-07-28
-
+## 3.17.1 - 2016-07-28
 * Fix auto-paging iterator so that it plays nicely with `iterator_to_array`
 
-### 3.17.0 2016-07-14
-
+## 3.17.0 - 2016-07-14
 * Add field annotations to model classes for better editor hinting
 
-### 3.16.0 2016-07-12
-
+## 3.16.0 - 2016-07-12
 * Add `ThreeDSecure` model for 3-D secure payments
 
-### 3.15.0 2016-06-29
-
+## 3.15.0 - 2016-06-29
 * Add static `update` method to all resources that can be changed.
 
-### 3.14.3 2016-06-20
-
+## 3.14.3 - 2016-06-20
 * Make sure that cURL never sends `Expects: 100-continue`, even on large request bodies
 
-### 3.14.2 2016-06-03
-
+## 3.14.2 - 2016-06-03
 * Add `inventory` under `SKU` to list of keys that have nested data and can be updated
 
-### 3.14.1 2016-05-27
-
+## 3.14.1 - 2016-05-27
 * Fix some inconsistencies in PHPDoc
 
-### 3.14.0 2016-05-25
-
+## 3.14.0 - 2016-05-25
 * Add support for returning Relay orders
 
-### 3.13.0 2016-05-04
-
+## 3.13.0 - 2016-05-04
 * Add `list`, `create`, `update`, `retrieve`, and `delete` methods to the Subscription class
 
-### 3.12.1 2016-04-07
-
+## 3.12.1 - 2016-04-07
 * Additional check on value arrays for some extra safety
 
-### 3.12.0 2016-03-31
-
+## 3.12.0 - 2016-03-31
 * Fix bug `refreshFrom` on `StripeObject` would not take an `$opts` array
 * Fix bug where `$opts` not passed to parent `save` method in `Account`
 * Fix bug where non-existent variable was referenced in `reverse` in `Transfer`
 * Update CA cert bundle for compatibility with OpenSSL versions below 1.0.1
 
-### 3.11.0 2016-03-22
-
+## 3.11.0 - 2016-03-22
 * Allow `CurlClient` to be initialized with default `CURLOPT_*` options
 
-### 3.10.1 2016-03-22
-
+## 3.10.1 - 2016-03-22
 * Fix bug where request params and options were ignored in `ApplicationFee`'s `refund.`
 
-### 3.10.0 2016-03-15
-
+## 3.10.0 - 2016-03-15
 * Add `reject` on `Account` to support the new API feature
 
-### 3.9.2 2016-03-04
-
+## 3.9.2 - 2016-03-04
 * Fix error when an object's metadata is set more than once
 
-### 3.9.1 2016-02-24
-
+## 3.9.1 - 2016-02-24
 * Fix encoding behavior of nested arrays for requests (see #227)
 
-### 3.9.0 2016-02-09
-
+## 3.9.0 - 2016-02-09
 * Add automatic pagination mechanism with `autoPagingIterator()`
 * Allow global account ID to be set with `Stripe::setAccountId()`
 
-### 3.8.0 2016-02-08
-
+## 3.8.0 - 2016-02-08
 * Add `CountrySpec` model for looking up country payment information
 
-### 3.7.1 2016-02-01
-
+## 3.7.1 - 2016-02-01
 * Update bundled CA certs
 
-### 3.7.0 2016-01-27
-
+## 3.7.0 - 2016-01-27
 * Support deleting Relay products and SKUs
 
-### 3.6.0 2016-01-05
-
+## 3.6.0 - 2016-01-05
 * Allow configuration of HTTP client timeouts
 
-### 3.5.0 2015-12-01
-
+## 3.5.0 - 2015-12-01
 * Add a verification routine for external accounts
 
-### 3.4.0 2015-09-14
-
+## 3.4.0 - 2015-09-14
 * Products, SKUs, and Orders -- https://stripe.com/relay
 
-### 3.3.0 2015-09-11
-
+## 3.3.0 - 2015-09-11
 * Add support for 429 Rate Limit response
 
-### 3.2.0 2015-08-17
-
+## 3.2.0 - 2015-08-17
 * Add refund listing and retrieval without an associated charge
 
-### 3.1.0 2015-08-03
-
+## 3.1.0 - 2015-08-03
 * Add dispute listing and retrieval
 * Add support for manage account deletion
 
-### 3.0.0 2015-07-28
-
+## 3.0.0 - 2015-07-28
 * Rename `\Stripe\Object` to `\Stripe\StripeObject` (PHP 7 compatibility)
 * Rename `getCode` and `getParam` in exceptions to `getStripeCode` and `getStripeParam`
 * Add support for calling `json_encode` on Stripe objects in PHP 5.4+
 * Start supporting/testing PHP 7
 
-### 2.3.0 2015-07-06
-
+## 2.3.0 - 2015-07-06
 * Add request ID to all Stripe exceptions
 
-### 2.2.0 2015-06-01
-
+## 2.2.0 - 2015-06-01
 * Add support for Alipay accounts as sources
 * Add support for bank accounts as sources (private beta)
 * Add support for bank accounts and cards as external_accounts on Account objects
 
-### 2.1.4 2015-05-13
-
+## 2.1.4 - 2015-05-13
 * Fix CA certificate file path (thanks @lphilps & @matthewarkin)
 
-### 2.1.3 2015-05-12
-
+## 2.1.3 - 2015-05-12
 * Fix to account updating to permit `tos_acceptance` and `personal_address` to be set properly
 * Fix to Transfer reversal creation (thanks @neatness!)
 * Network requests are now done through a swappable class for easier mocking
 
-### 2.1.2 2015-04-10
-
+## 2.1.2 - 2015-04-10
 * Remove SSL cert revokation checking (all pre-Heartbleed certs have expired)
 * Bug fixes to account updating
 
-### 2.1.1 2015-02-27
+## 2.1.1 - 2015-02-27
 * Support transfer reversals
 
-### 2.1.0 2015-02-19
-
+## 2.1.0 - 2015-02-19
 * Support new API version (2015-02-18)
 * Added Bitcoin Receiever update and delete actions
 * Edited tests to prefer "source" over "card" as per new API version
 
-### 2.0.1 2015-02-16
-
+## 2.0.1 - 2015-02-16
 * Fix to fetching endpoints that use a non-default baseUrl (`FileUpload`)
 
-### 2.0.0 2015-02-14
-
+## 2.0.0 - 2015-02-14
 * Bumped minimum version to 5.3.3
 * Switched to Stripe namespace instead of Stripe_ class name prefiexes (thanks @chadicus!)
 * Switched tests to PHPUnit (thanks @chadicus!)
@@ -215,192 +383,134 @@
 * Added $opts hash to the end of most methods: this permits passing 'idempotency_key', 'stripe_account', or 'stripe_version'. The last 2 will persist across multiple object loads.
 * Added support for retrieving Account by ID
 
-### 1.18.0 2015-01-21
-
+## 1.18.0 - 2015-01-21
 * Support making bitcoin charges through BitcoinReceiver source object
 
-### 1.17.5 2014-12-23
-
+## 1.17.5 - 2014-12-23
 * Adding support for creating file uploads.
 
-### 1.17.4 2014-12-15
-
+## 1.17.4 - 2014-12-15
 * Saving objects fetched with a custom key now works (thanks @JustinHook & @jpasilan)
 * Added methods for reporting charges as safe or fraudulent and for specifying the reason for refunds
 
-### 1.17.3 2014-11-06
-
+## 1.17.3 - 2014-11-06
 * Better handling of HHVM support for SSL certificate blacklist checking.
 
-### 1.17.2 2014-09-23
-
+## 1.17.2 - 2014-09-23
 * Coupons now are backed by a `Stripe_Coupon` instead of `Stripe_Object`, and support updating metadata
 * Running operations (`create`, `retrieve`, `all`) on upcoming invoice items now works
 
-### 1.17.1 2014-07-31
-
+## 1.17.1 - 2014-07-31
 * Requests now send Content-Type header
 
-### 1.17.0 2014-07-29
-
+## 1.17.0 - 2014-07-29
 * Application Fee refunds now a list instead of array
 * HHVM now works
 * Small bug fixes (thanks @bencromwell & @fastest963)
-* __toString now returns the name of the object in addition to its JSON representation
-
-### 1.16.0 2014-06-17
+* `__toString` now returns the name of the object in addition to its JSON representation
 
+## 1.16.0 - 2014-06-17
 * Add metadata for refunds and disputes
 
-### 1.15.0 2014-05-28
-
+## 1.15.0 - 2014-05-28
 * Support canceling transfers
 
-### 1.14.1 2014-05-21
-
+## 1.14.1 - 2014-05-21
 * Support cards for recipients.
 
-### 1.13.1 2014-05-15
-
+## 1.13.1 - 2014-05-15
 * Fix bug in account resource where `id` wasn't in the result
 
-### 1.13.0 2014-04-10
-
+## 1.13.0 - 2014-04-10
 * Add support for certificate blacklisting
 * Update ca bundle
 * Drop support for HHVM (Temporarily)
 
-### 1.12.0 2014-04-01
-
+## 1.12.0 - 2014-04-01
 * Add Stripe_RateLimitError for catching rate limit errors.
 * Update to Zend coding style (thanks,  @jpiasetz)
 
-### 1.11.0 2014-01-29
-
+## 1.11.0 - 2014-01-29
 * Add support for multiple subscriptions per customer
 
-### 1.10.1 2013-12-02
-
+## 1.10.1 - 2013-12-02
 * Add new ApplicationFee
 
-### 1.9.1 2013-11-08
-
+## 1.9.1 - 2013-11-08
 * Fix a bug where a null nestable object causes warnings to fire.
 
-### 1.9.0 2013-10-16
-
+## 1.9.0 - 2013-10-16
 * Add support for metadata API.
 
-### 1.8.4 2013-09-18
-
+## 1.8.4 - 2013-09-18
 * Add support for closing disputes.
 
-### 1.8.3 2013-08-13
-
+## 1.8.3 - 2013-08-13
 * Add new Balance and BalanceTransaction
 
-### 1.8.2 2013-08-12
-
-* Add support for unsetting attributes by updating to NULL.
-  Setting properties to a blank string is now an error.
-
-### 1.8.1 2013-07-12
+## 1.8.2 - 2013-08-12
+* Add support for unsetting attributes by updating to NULL. Setting properties to a blank string is now an error.
 
+## 1.8.1 - 2013-07-12
 * Add support for multiple cards API (Stripe API version 2013-07-12: https://stripe.com/docs/upgrades#2013-07-05)
 
-### 1.8.0 2013-04-11
-
+## 1.8.0 - 2013-04-11
 * Allow Transfers to be creatable
 * Add new Recipient resource
 
-### 1.7.15 2013-02-21
-
+## 1.7.15 - 2013-02-21
 * Add 'id' to the list of permanent object attributes
 
-### 1.7.14 2013-02-20
-
-* Don't re-encode strings that are already encoded in UTF-8. If you
-  were previously using plan or coupon objects with UTF-8 IDs, they
-  may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a
-  2nd time. You may now need to pass the IDs to utf8_encode before
-  passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve.
-* Ensure that all input is encoded in UTF-8 before submitting it to
-  Stripe's servers. (github issue #27)
-
-### 1.7.13 2013-02-01
+## 1.7.14 - 2013-02-20
 
-* Add support for passing options when retrieving Stripe objects
-  e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer")))
-  Stripe_Charge::retrieve("foo") will continue to work
+* Don't re-encode strings that are already encoded in UTF-8. If you were previously using plan or coupon objects with UTF-8 IDs, they may have been treated as ISO-8859-1 (Latin-1) and encoded to UTF-8 a 2nd time. You may now need to pass the IDs to utf8_encode before passing them to Stripe_Plan::retrieve or Stripe_Coupon::retrieve.
+* Ensure that all input is encoded in UTF-8 before submitting it to Stripe's servers. (github issue #27)
 
-### 1.7.12 2013-01-15
+## 1.7.13 - 2013-02-01
+* Add support for passing options when retrieving Stripe objects e.g., Stripe_Charge::retrieve(array("id"=>"foo", "expand" => array("customer"))); Stripe_Charge::retrieve("foo") will continue to work
 
+## 1.7.12 - 2013-01-15
 * Add support for setting a Stripe API version override
 
-### 1.7.11 2012-12-30
-
-* Version bump to cleanup constants and such (github issue #26)
-
-### 1.7.10 2012-11-08
+## 1.7.11 - 2012-12-30
+* Version bump to cleanup constants and such (fix issue #26)
 
+## 1.7.10 - 2012-11-08
 * Add support for updating charge disputes.
 * Fix bug preventing retrieval of null attributes
 
-### 1.7.9 2012-11-08
+## 1.7.9 - 2012-11-08
+* Fix usage under autoloaders such as the one generated by composer (fix issue #22)
 
-* Fix usage under autoloaders such as the one generated by composer
-  (github issue #22)
-
-### 1.7.8 2012-10-30
+## 1.7.8 - 2012-10-30
 * Add support for creating invoices.
 * Add support for new invoice lines return format
 * Add support for new list objects
 
-### 1.7.7 2012-09-14
-
-* Get all of the various version numbers in the repo in sync (no other
-  changes)
-
-### 1.7.6 2012-08-31
+## 1.7.7 - 2012-09-14
+* Get all of the various version numbers in the repo in sync (no other changes)
 
+## 1.7.6 - 2012-08-31
 * Add update and pay methods to Invoice resource
 
-### 1.7.5 2012-08-23
-
-* Change internal function names so that Stripe_SingletonApiRequest is
-  E_STRICT-clean (github issue #16)
+## 1.7.5 - 2012-08-23
+* Change internal function names so that Stripe_SingletonApiRequest is E_STRICT-clean (github issue #16)
 
-### 1.7.4 2012-08-21
-
-* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used
-  in API calls are transparently converted to their object IDs
-
-### 1.7.3 2012-08-15
+## 1.7.4 - 2012-08-21
+* Bugfix so that Stripe objects (e.g. Customer, Charge objects) used in API calls are transparently converted to their object IDs
 
+## 1.7.3 - 2012-08-15
 * Add new Account resource
 
-### 1.7.2 2012-06-26
-
-* Make clearer that you should be including lib/Stripe.php, not
-  test/Stripe.php (github issue #14)
-
-### 1.7.1 2012-05-24
-
-* Add missing argument to Stripe_InvalidRequestError constructor in
-  Stripe_ApiResource::instanceUrl. Fixes a warning when
-  Stripe_ApiResource::instanceUrl is called on a resource with no ID
-  (github issue #12)
+## 1.7.2 - 2012-06-26
+* Make clearer that you should be including lib/Stripe.php, not test/Stripe.php (github issue #14)
 
-### 1.7.0 2012-05-17
+## 1.7.1 - 2012-05-24
+* Add missing argument to Stripe_InvalidRequestError constructor in Stripe_ApiResource::instanceUrl. Fixes a warning when Stripe_ApiResource::instanceUrl is called on a resource with no ID (fix issue #12)
 
+## 1.7.0 - 2012-05-17
 * Support Composer and Packagist (github issue #9)
-
 * Add new deleteDiscount method to Stripe_Customer
-
 * Add new Transfer resource
-
-* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will
-  support Basic auth for the indefinite future, but recommends Bearer
-  auth when possible going forward)
-
+* Switch from using HTTP Basic auth to Bearer auth. (Note: Stripe will support Basic auth for the indefinite future, but recommends Bearer auth when possible going forward)
 * Numerous test suite improvements

+ 100 - 12
plugins/stripe/README.md

@@ -10,7 +10,7 @@ You can sign up for a Stripe account at https://stripe.com.
 
 ## Requirements
 
-PHP 5.3.3 and later.
+PHP 5.4.0 and later.
 
 ## Composer
 
@@ -20,7 +20,7 @@ You can install the bindings via [Composer](http://getcomposer.org/). Run the fo
 composer require stripe/stripe-php
 ```
 
-To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/00-intro.md#autoloading):
+To use the bindings, use Composer's [autoload](https://getcomposer.org/doc/01-basic-usage.md#autoloading):
 
 ```php
 require_once('vendor/autoload.php');
@@ -36,7 +36,7 @@ require_once('/path/to/stripe-php/init.php');
 
 ## Dependencies
 
-The bindings require the following extension in order to work properly:
+The bindings require the following extensions in order to work properly:
 
 - [`curl`](https://secure.php.net/manual/en/book.curl.php), although you can use your own non-cURL client if you prefer
 - [`json`](https://secure.php.net/manual/en/book.json.php)
@@ -49,9 +49,8 @@ If you use Composer, these dependencies should be handled automatically. If you
 Simple usage looks like:
 
 ```php
-\Stripe\Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
-$myCard = array('number' => '4242424242424242', 'exp_month' => 8, 'exp_year' => 2018);
-$charge = \Stripe\Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
+\Stripe\Stripe::setApiKey('sk_test_BQokikJOvBiI2HlWgH4olfQ2');
+$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_189fqt2eZvKYlo2CTGBeg6Uq']);
 echo $charge;
 ```
 
@@ -61,14 +60,19 @@ Please see https://stripe.com/docs/api for up-to-date documentation.
 
 ## Legacy Version Support
 
+### PHP 5.3
+
+If you are using PHP 5.3, you can download v5.9.2 ([zip](https://github.com/stripe/stripe-php/archive/v5.9.2.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v5.9.2.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
+
+### PHP 5.2
+
 If you are using PHP 5.2, you can download v1.18.0 ([zip](https://github.com/stripe/stripe-php/archive/v1.18.0.zip), [tar.gz](https://github.com/stripe/stripe-php/archive/v1.18.0.tar.gz)) from our [releases page](https://github.com/stripe/stripe-php/releases). This version will continue to work with new versions of the Stripe API for all common uses.
 
 This legacy version may be included via `require_once("/path/to/stripe-php/lib/Stripe.php");`, and used like:
 
 ```php
 Stripe::setApiKey('d8e8fca2dc0f896fd7cb4cb0031ba249');
-$myCard = array('number' => '4242424242424242', 'exp_month' => 8, 'exp_year' => 2018);
-$charge = Stripe_Charge::create(array('card' => $myCard, 'amount' => 2000, 'currency' => 'usd'));
+$charge = Stripe_Charge::create(array('source' => 'tok_XXXXXXXX', 'amount' => 2000, 'currency' => 'usd'));
 echo $charge;
 ```
 
@@ -99,13 +103,32 @@ Need to set a proxy for your requests? Pass in the requisite `CURLOPT_*` array t
 
 ```php
 // set up your tweaked Curl client
-$curl = new \Stripe\HttpClient\CurlClient(array(CURLOPT_PROXY => 'proxy.local:80'));
+$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_PROXY => 'proxy.local:80']);
 // tell Stripe to use the tweaked client
 \Stripe\ApiRequestor::setHttpClient($curl);
 ```
 
 Alternately, a callable can be passed to the CurlClient constructor that returns the above array based on request inputs. See `testDefaultOptions()` in `tests/CurlClientTest.php` for an example of this behavior. Note that the callable is called at the beginning of every API request, before the request is sent.
 
+### Configuring a Logger
+
+The library does minimal logging, but it can be configured
+with a [`PSR-3` compatible logger][psr3] so that messages
+end up there instead of `error_log`:
+
+```php
+\Stripe\Stripe::setLogger($logger);
+```
+
+### Accessing response data
+
+You can access the data from the last API response on any object via `getLastResponse()`.
+
+```php
+$charge = \Stripe\Charge::create(['amount' => 2000, 'currency' => 'usd', 'source' => 'tok_visa']);
+echo $charge->getLastResponse()->headers['Request-Id'];
+```
+
 ### SSL / TLS compatibility issues
 
 Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/blog/upgrading-tls). Some systems (most notably some older CentOS and RHEL versions) are capable of using TLS 1.2 but will use TLS 1.0 or 1.1 by default. In this case, you'd get an `invalid_request_error` with the following error message: "Stripe no longer supports API requests made with TLS 1.0. Please initiate HTTPS connections with TLS 1.2 or later. You can learn more about this at [https://stripe.com/blog/upgrading-tls](https://stripe.com/blog/upgrading-tls).".
@@ -113,19 +136,71 @@ Stripe's API now requires that [all connections use TLS 1.2](https://stripe.com/
 The recommended course of action is to [upgrade your cURL and OpenSSL packages](https://support.stripe.com/questions/how-do-i-upgrade-my-stripe-integration-from-tls-1-0-to-tls-1-2#php) so that TLS 1.2 is used by default, but if that is not possible, you might be able to solve the issue by setting the `CURLOPT_SSLVERSION` option to either `CURL_SSLVERSION_TLSv1` or `CURL_SSLVERSION_TLSv1_2`:
 
 ```php
-$curl = new \Stripe\HttpClient\CurlClient(array(CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1));
+$curl = new \Stripe\HttpClient\CurlClient([CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1]);
 \Stripe\ApiRequestor::setHttpClient($curl);
 ```
 
+### Per-request Configuration
+
+For apps that need to use multiple keys during the lifetime of a process, like
+one that uses [Stripe Connect][connect], it's also possible to set a
+per-request key and/or account:
+
+```php
+\Stripe\Charge::all([], [
+    'api_key' => 'sk_test_...',
+    'stripe_account' => 'acct_...'
+]);
+
+\Stripe\Charge::retrieve("ch_18atAXCdGbJFKhCuBAa4532Z", [
+    'api_key' => 'sk_test_...',
+    'stripe_account' => 'acct_...'
+]);
+```
+
+### Configuring CA Bundles
+
+By default, the library will use its own internal bundle of known CA
+certificates, but it's possible to configure your own:
+
+```php
+\Stripe\Stripe::setCABundlePath("path/to/ca/bundle");
+```
+
+### Configuring Automatic Retries
+
+The library can be configured to automatically retry requests that fail due to
+an intermittent network problem:
+
+```php
+\Stripe\Stripe::setMaxNetworkRetries(2);
+```
+
+[Idempotency keys][idempotency-keys] are added to requests to guarantee that
+retries are safe.
+
 ## Development
 
+Get [Composer][composer]. For example, on Mac OS:
+
+```bash
+brew install composer
+```
+
 Install dependencies:
 
-``` bash
+```bash
 composer install
 ```
 
-## Tests
+The test suite depends on [stripe-mock], so make sure to fetch and run it from a
+background terminal ([stripe-mock's README][stripe-mock] also contains
+instructions for installing via Homebrew and other methods):
+
+```bash
+go get -u github.com/stripe/stripe-mock
+stripe-mock
+```
 
 Install dependencies as mentioned above (which will resolve [PHPUnit](http://packagist.org/packages/phpunit/phpunit)), then you can run the test suite:
 
@@ -139,6 +214,12 @@ Or to run an individual test file:
 ./vendor/bin/phpunit tests/UtilTest.php
 ```
 
+Update bundled CA certificates from the [Mozilla cURL release][curl]:
+
+```bash
+./update_certs.php
+```
+
 ## Attention plugin developers
 
 Are you writing a plugin that integrates Stripe and embeds our library? Then please use the `setAppInfo` function to identify your plugin. For example:
@@ -152,3 +233,10 @@ The method should be called once, before any request is sent to the API. The sec
 ### SSL / TLS configuration option
 
 See the "SSL / TLS compatibility issues" paragraph above for full context. If you want to ensure that your plugin can be used on all systems, you should add a configuration option to let your users choose between different values for `CURLOPT_SSLVERSION`: none (default), `CURL_SSLVERSION_TLSv1` and `CURL_SSLVERSION_TLSv1_2`.
+
+[composer]: https://getcomposer.org/
+[connect]: https://stripe.com/connect
+[curl]: http://curl.haxx.se/docs/caextract.html
+[psr3]: http://www.php-fig.org/psr/psr-3/
+[idempotency-keys]: https://stripe.com/docs/api/php#idempotent_requests
+[stripe-mock]: https://github.com/stripe/stripe-mock

+ 1 - 1
plugins/stripe/VERSION

@@ -1 +1 @@
-4.4.0
+6.15.0

+ 1 - 1
plugins/stripe/build.php

@@ -10,7 +10,7 @@ if (!$autoload) {
     $composer = json_decode(file_get_contents('composer.json'), true);
     unset($composer['autoload']);
     unset($composer['require-dev']['squizlabs/php_codesniffer']);
-    file_put_contents('composer.json', json_encode($composer));
+    file_put_contents('composer.json', json_encode($composer, JSON_PRETTY_PRINT));
 }
 
 passthru('composer install', $returnStatus);

+ 1 - 1
plugins/stripe/composer.json

@@ -15,7 +15,7 @@
     }
   ],
   "require": {
-    "php": ">=5.3.3",
+    "php": ">=5.4.0",
     "ext-curl": "*",
     "ext-json": "*",
     "ext-mbstring": "*"

+ 421 - 694
plugins/stripe/data/ca-certificates.crt

@@ -1,20 +1,20 @@
 ##
 ## Bundle of CA Root Certificates
 ##
-## Certificate data from Mozilla as of: Fri Jan 22 20:39:57 2016
+## Certificate data from Mozilla as of: Wed Sep 20 03:12:05 2017 GMT
 ##
 ## This is a bundle of X.509 certificates of public Certificate Authorities
 ## (CA). These were automatically extracted from Mozilla's root certificates
 ## file (certdata.txt).  This file can be found in the mozilla source tree:
-## http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
+## https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt
 ##
 ## It contains the certificates in PEM format and therefore
 ## can be directly used with curl / libcurl / php_curl, or with
 ## an Apache+mod_ssl webserver for SSL client authentication.
 ## Just configure this file as the SSLCACertificateFile.
 ##
-## Conversion done with mk-ca-bundle.pl version 1.25.
-## SHA1: 0ab47e2f41518f8d223eab517cb799e5b071231e
+## Conversion done with mk-ca-bundle.pl version 1.27.
+## SHA256: 2b2dbe5244e0047e088c597998883a913f6c5fffd1cb5c0fe5a368c8466cb2ec
 ##
 
 
@@ -130,30 +130,6 @@ Y71k5h+3zvDyny67G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
 RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
 -----END CERTIFICATE-----
 
-AddTrust Low-Value Services Root
-================================
------BEGIN CERTIFICATE-----
-MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRU
-cnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQsw
-CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBO
-ZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEB
-AQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY6
-54eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWr
-oulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1
-Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJui
-GMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8w
-HQYDVR0OBBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTAD
-AQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQswCQYDVQQGEwJT
-RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEw
-HwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxt
-ZBsfzQ3duQH6lmM0MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
-iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY
-eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJlpz/+0WatC7xr
-mYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vj
-ccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk=
------END CERTIFICATE-----
-
 AddTrust External Root
 ======================
 -----BEGIN CERTIFICATE-----
@@ -178,54 +154,6 @@ e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4u
 G+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
 -----END CERTIFICATE-----
 
-AddTrust Public Services Root
-=============================
------BEGIN CERTIFICATE-----
-MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSAwHgYDVQQDExdBZGRU
-cnVzdCBQdWJsaWMgQ0EgUm9vdDAeFw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJ
-BgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5l
-dHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEF
-AAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbu
-nyNu9DnLoblv8n75XYcmYZ4c+OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1i
-d9NEHif2P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKXC1sIXzSG
-Aa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8Rs3iQhCBSWxHveNCD9tVIkNAw
-HM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0G
-A1UdDgQWBBSBPjfYkrAfd59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zCBjgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkGA1UEBhMCU0Ux
-FDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29yazEgMB4G
-A1UEAxMXQWRkVHJ1c3QgUHVibGljIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4
-JNojVhaTdt02KLmuG7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
-+YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbjPGsye/Kf8Lb93/Ao
-GEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bYGozH7ZxOmuASu7VqTITh4SINhwBk/ox9
-Yjllpu9CtoAlEmEBqCQTcAARJl/6NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9H
-EufOX1362KqxMy3ZdvJOOjMMK7MtkAY=
------END CERTIFICATE-----
-
-AddTrust Qualified Certificates Root
-====================================
------BEGIN CERTIFICATE-----
-MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEUMBIGA1UEChML
-QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSMwIQYDVQQDExpBZGRU
-cnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcx
-CzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
-IE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG
-9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx
-64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3
-KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1tUvznoD1o
-L/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GR
-wVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HU
-MIHRMB0GA1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/
-BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkwZzELMAkGA1UE
-BhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRUcnVzdCBUVFAgTmV0d29y
-azEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQAD
-ggEBABmrder4i2VhlRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
-GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X
-dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3P6CxB9bpT9ze
-RXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDB
-iFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
------END CERTIFICATE-----
-
 Entrust Root Certification Authority
 ====================================
 -----BEGIN CERTIFICATE-----
@@ -252,27 +180,6 @@ W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0
 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8
 -----END CERTIFICATE-----
 
-RSA Security 2048 v3
-====================
------BEGIN CERTIFICATE-----
-MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUFADA6MRkwFwYDVQQK
-ExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0EgU2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAy
-MjIyMDM5MjNaFw0yNjAyMjIyMDM5MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAb
-BgNVBAsTFFJTQSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
-AQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37RqtBaB4Y6lXIL5F4iSj7
-Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgb
-WhOHV4PR8CDn6E8jQrAApX2J6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iH
-KrtjEAMqs6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzDuvsf9/UP
-+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/
-MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4E
-FgQUB8NRMKSq6UWuNST6/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmY
-v/3VEhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5gEydxiKRz44Rj
-0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+f00/FGj1EVDVwfSQpQgdMWD/YIwj
-VAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJqaHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395
-nzIlQnQFgCi/vcEkllgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
-pKnXwiJPZ9d37CAFYd4=
------END CERTIFICATE-----
-
 GeoTrust Global CA
 ==================
 -----BEGIN CERTIFICATE-----
@@ -294,27 +201,6 @@ XE0zX5IJL4hmXXeXxx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
 Mw==
 -----END CERTIFICATE-----
 
-GeoTrust Global CA 2
-====================
------BEGIN CERTIFICATE-----
-MIIDZjCCAk6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMN
-R2VvVHJ1c3QgSW5jLjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwHhcNMDQwMzA0MDUw
-MDAwWhcNMTkwMzA0MDUwMDAwWjBEMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5j
-LjEdMBsGA1UEAxMUR2VvVHJ1c3QgR2xvYmFsIENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDvPE1APRDfO1MA4Wf+lGAVPoWI8YkNkMgoI5kF6CsgncbzYEbYwbLVjDHZ3CB5JIG/
-NTL8Y2nbsSpr7iFY8gjpeMtvy/wWUsiRxP89c96xPqfCfWbB9X5SJBri1WeR0IIQ13hLTytCOb1k
-LUCgsBDTOEhGiKEMuzozKmKY+wCdE1l/bztyqu6mD4b5BWHqZ38MN5aL5mkWRxHCJ1kDs6ZgwiFA
-Vvqgx306E+PsV8ez1q6diYD3Aecs9pYrEw15LNnA5IZ7S4wMcoKK+xfNAGw6EzywhIdLFnopsk/b
-HdQL82Y3vdj2V7teJHq4PIu5+pIaGoSe2HSPqht/XvT+RSIhAgMBAAGjYzBhMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFHE4NvICMVNHK266ZUapEBVYIAUJMB8GA1UdIwQYMBaAFHE4NvICMVNH
-K266ZUapEBVYIAUJMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAQEAA/e1K6tdEPx7
-srJerJsOflN4WT5CBP51o62sgU7XAotexC3IUnbHLB/8gTKY0UvGkpMzNTEv/NgdRN3ggX+d6Yvh
-ZJFiCzkIjKx0nVnZellSlxG5FntvRdOW2TF9AjYPnDtuzywNA0ZF66D0f0hExghAzN4bcLUprbqL
-OzRldRtxIR0sFAqwlpW41uryZfspuk/qkZN0abby/+Ea0AzRdoXLiiW9l14sbxWZJue2Kf8i7MkC
-x1YAzUm5s2x7UwQa4qjJqhIFI8LO57sEAszAR6LkxCkvW0VXiVHuPOtSCP8HNR6fNWpHSlaY0VqF
-H4z1Ir+rzoPz4iIprn2DQKi6bA==
------END CERTIFICATE-----
-
 GeoTrust Universal CA
 =====================
 -----BEGIN CERTIFICATE-----
@@ -440,56 +326,6 @@ Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2G9w84FoVxp7Z
 12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
 -----END CERTIFICATE-----
 
-Comodo Secure Services root
-===========================
------BEGIN CERTIFICATE-----
-MIIEPzCCAyegAwIBAgIBATANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDEkMCIGA1UEAwwbU2VjdXJlIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAw
-MDAwMFoXDTI4MTIzMTIzNTk1OVowfjELMAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFu
-Y2hlc3RlcjEQMA4GA1UEBwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxJDAi
-BgNVBAMMG1NlY3VyZSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBAMBxM4KK0HDrc4eCQNUd5MvJDkKQ+d40uaG6EfQlhfPMcm3ye5drswfxdySRXyWP
-9nQ95IDC+DwN879A6vfIUtFyb+/Iq0G4bi4XKpVpDM3SHpR7LZQdqnXXs5jLrLxkU0C8j6ysNstc
-rbvd4JQX7NFc0L/vpZXJkMWwrPsbQ996CF23uPJAGysnnlDOXmWCiIxe004MeuoIkbY2qitC++rC
-oznl2yY4rYsK7hljxxwk3wN42ubqwUcaCwtGCd0C/N7Lh1/XMGNooa7cMqG6vv5Eq2i2pRcV/b3V
-p6ea5EQz6YiO/O1R65NxTq0B50SOqy3LqP4BSUjwwN3HaNiS/j0CAwEAAaOBxzCBxDAdBgNVHQ4E
-FgQUPNiTiMLAggnMAZkGkyDpnnAJY08wDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w
-gYEGA1UdHwR6MHgwO6A5oDeGNWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL1NlY3VyZUNlcnRpZmlj
-YXRlU2VydmljZXMuY3JsMDmgN6A1hjNodHRwOi8vY3JsLmNvbW9kby5uZXQvU2VjdXJlQ2VydGlm
-aWNhdGVTZXJ2aWNlcy5jcmwwDQYJKoZIhvcNAQEFBQADggEBAIcBbSMdflsXfcFhMs+P5/OKlFlm
-4J4oqF7Tt/Q05qo5spcWxYJvMqTpjOev/e/C6LlLqqP05tqNZSH7uoDrJiiFGv45jN5bBAS0VPmj
-Z55B+glSzAVIqMk/IQQezkhr/IXownuvf7fM+F86/TXGDe+X3EyrEeFryzHRbPtIgKvcnDe4IRRL
-DXE97IMzbtFuMhbsmMcWi1mmNKsFVy2T96oTy9IT4rcuO81rUBcJaD61JlfutuC23bkpgHl9j6Pw
-pCikFcSF9CfUa7/lXORlAnZUtOM3ZiTTGWHIUhDlizeauan5Hb/qmZJhlv8BzaFfDbxxvA6sCx1H
-RR3B7Hzs/Sk=
------END CERTIFICATE-----
-
-Comodo Trusted Services root
-============================
------BEGIN CERTIFICATE-----
-MIIEQzCCAyugAwIBAgIBATANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEbMBkGA1UECAwS
-R3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0Eg
-TGltaXRlZDElMCMGA1UEAwwcVHJ1c3RlZCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczAeFw0wNDAxMDEw
-MDAwMDBaFw0yODEyMzEyMzU5NTlaMH8xCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1h
-bmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVkMSUw
-IwYDVQQDDBxUcnVzdGVkIENlcnRpZmljYXRlIFNlcnZpY2VzMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEA33FvNlhTWvI2VFeAxHQIIO0Yfyod5jWaHiWsnOWWfnJSoBVC21ndZHoa0Lh7
-3TkVvFVIxO06AOoxEbrycXQaZ7jPM8yoMa+j49d/vzMtTGo87IvDktJTdyR0nAducPy9C1t2ul/y
-/9c3S0pgePfw+spwtOpZqqPOSC+pw7ILfhdyFgymBwwbOM/JYrc/oJOlh0Hyt3BAd9i+FHzjqMB6
-juljatEPmsbS9Is6FARW1O24zG71++IsWL1/T2sr92AkWCTOJu80kTrV44HQsvAEAtdbtz6SrGsS
-ivnkBbA7kUlcsutT6vifR4buv5XAwAaf0lteERv0xwQ1KdJVXOTt6wIDAQABo4HJMIHGMB0GA1Ud
-DgQWBBTFe1i97doladL3WRaoszLAeydb9DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
-/zCBgwYDVR0fBHwwejA8oDqgOIY2aHR0cDovL2NybC5jb21vZG9jYS5jb20vVHJ1c3RlZENlcnRp
-ZmljYXRlU2VydmljZXMuY3JsMDqgOKA2hjRodHRwOi8vY3JsLmNvbW9kby5uZXQvVHJ1c3RlZENl
-cnRpZmljYXRlU2VydmljZXMuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQDIk4E7ibSvuIQSTI3S8Ntw
-uleGFTQQuS9/HrCoiWChisJ3DFBKmwCL2Iv0QeLQg4pKHBQGsKNoBXAxMKdTmw7pSqBYaWcOrp32
-pSxBvzwGa+RZzG0Q8ZZvH9/0BAKkn0U+yNj6NkZEUD+Cl5EfKNsYEYwq5GWDVxISjBc/lDb+XbDA
-BHcTuPQV1T84zJQ6VdCsmPW6AF/ghhmBeC8owH7TzEIK9a5QoNE+xqFx7D+gIIxmOom0jtTYsU0l
-R+4viMi14QVFwL4Ucd56/Y57fU0IlqUSc/AtyjcndBInTMu2l+nZrghtWjlA3QVHdWpaIbOjGM9O
-9y5Xt5hwXsjEeLBi
------END CERTIFICATE-----
-
 QuoVadis Root CA
 ================
 -----BEGIN CERTIFICATE-----
@@ -629,54 +465,6 @@ EtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLH
 llpwrN9M
 -----END CERTIFICATE-----
 
-Staat der Nederlanden Root CA
-=============================
------BEGIN CERTIFICATE-----
-MIIDujCCAqKgAwIBAgIEAJiWijANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJOTDEeMBwGA1UE
-ChMVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSYwJAYDVQQDEx1TdGFhdCBkZXIgTmVkZXJsYW5kZW4g
-Um9vdCBDQTAeFw0wMjEyMTcwOTIzNDlaFw0xNTEyMTYwOTE1MzhaMFUxCzAJBgNVBAYTAk5MMR4w
-HAYDVQQKExVTdGFhdCBkZXIgTmVkZXJsYW5kZW4xJjAkBgNVBAMTHVN0YWF0IGRlciBOZWRlcmxh
-bmRlbiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmNK1URF6gaYUmHFt
-vsznExvWJw56s2oYHLZhWtVhCb/ekBPHZ+7d89rFDBKeNVU+LCeIQGv33N0iYfXCxw719tV2U02P
-jLwYdjeFnejKScfST5gTCaI+Ioicf9byEGW07l8Y1Rfj+MX94p2i71MOhXeiD+EwR+4A5zN9RGca
-C1Hoi6CeUJhoNFIfLm0B8mBF8jHrqTFoKbt6QZ7GGX+UtFE5A3+y3qcym7RHjm+0Sq7lr7HcsBth
-vJly3uSJt3omXdozSVtSnA71iq3DuD3oBmrC1SoLbHuEvVYFy4ZlkuxEK7COudxwC0barbxjiDn6
-22r+I/q85Ej0ZytqERAhSQIDAQABo4GRMIGOMAwGA1UdEwQFMAMBAf8wTwYDVR0gBEgwRjBEBgRV
-HSAAMDwwOgYIKwYBBQUHAgEWLmh0dHA6Ly93d3cucGtpb3ZlcmhlaWQubmwvcG9saWNpZXMvcm9v
-dC1wb2xpY3kwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSofeu8Y6R0E3QA7Jbg0zTBLL9s+DAN
-BgkqhkiG9w0BAQUFAAOCAQEABYSHVXQ2YcG70dTGFagTtJ+k/rvuFbQvBgwp8qiSpGEN/KtcCFtR
-EytNwiphyPgJWPwtArI5fZlmgb9uXJVFIGzmeafR2Bwp/MIgJ1HI8XxdNGdphREwxgDS1/PTfLbw
-MVcoEoJz6TMvplW0C5GUR5z6u3pCMuiufi3IvKwUv9kP2Vv8wfl6leF9fpb8cbDCTMjfRTTJzg3y
-nGQI0DvDKcWy7ZAEwbEpkcUwb8GpcjPM/l0WFywRaed+/sWDCN+83CI6LiBpIzlWYGeQiy52OfsR
-iJf2fL1LuCAWZwWN4jvBcj+UlTfHXbme2JOhF4//DGYVwSR8MnwDHTuhWEUykw==
------END CERTIFICATE-----
-
-UTN USERFirst Hardware Root CA
-==============================
------BEGIN CERTIFICATE-----
-MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UE
-BhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhl
-IFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAd
-BgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgx
-OTIyWjCBlzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0
-eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVz
-ZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdhcmUwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlI
-wrthdBKWHTxqctU8EGc6Oe0rE81m65UJM6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFd
-tqdt++BxF2uiiPsA3/4aMXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8
-i4fDidNdoI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqIDsjf
-Pe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9KsyoUhbAgMBAAGjgbkw
-gbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFKFyXyYbKJhDlV0HN9WF
-lp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNF
-UkZpcnN0LUhhcmR3YXJlLmNybDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUF
-BwMGBggrBgEFBQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM
-//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28GpgoiskliCE7/yMgUsogW
-XecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gECJChicsZUN/KHAG8HQQZexB2
-lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kn
-iCrVWFCVH/A7HFe7fRQ5YiuayZSSKqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67
-nfhmqA==
------END CERTIFICATE-----
-
 Camerfirma Chambers of Commerce Root
 ====================================
 -----BEGIN CERTIFICATE-----
@@ -731,41 +519,6 @@ IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
 t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
 -----END CERTIFICATE-----
 
-NetLock Notary (Class A) Root
-=============================
------BEGIN CERTIFICATE-----
-MIIGfTCCBWWgAwIBAgICAQMwDQYJKoZIhvcNAQEEBQAwga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQI
-EwdIdW5nYXJ5MREwDwYDVQQHEwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6
-dG9uc2FnaSBLZnQuMRowGAYDVQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9j
-ayBLb3pqZWd5em9pIChDbGFzcyBBKSBUYW51c2l0dmFueWtpYWRvMB4XDTk5MDIyNDIzMTQ0N1oX
-DTE5MDIxOTIzMTQ0N1owga8xCzAJBgNVBAYTAkhVMRAwDgYDVQQIEwdIdW5nYXJ5MREwDwYDVQQH
-EwhCdWRhcGVzdDEnMCUGA1UEChMeTmV0TG9jayBIYWxvemF0Yml6dG9uc2FnaSBLZnQuMRowGAYD
-VQQLExFUYW51c2l0dmFueWtpYWRvazE2MDQGA1UEAxMtTmV0TG9jayBLb3pqZWd5em9pIChDbGFz
-cyBBKSBUYW51c2l0dmFueWtpYWRvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvHSM
-D7tM9DceqQWC2ObhbHDqeLVu0ThEDaiDzl3S1tWBxdRL51uUcCbbO51qTGL3cfNk1mE7PetzozfZ
-z+qMkjvN9wfcZnSX9EUi3fRc4L9t875lM+QVOr/bmJBVOMTtplVjC7B4BPTjbsE/jvxReB+SnoPC
-/tmwqcm8WgD/qaiYdPv2LD4VOQ22BFWoDpggQrOxJa1+mm9dU7GrDPzr4PN6s6iz/0b2Y6LYOph7
-tqyF/7AlT3Rj5xMHpQqPBffAZG9+pyeAlt7ULoZgx2srXnN7F+eRP2QM2EsiNCubMvJIH5+hCoR6
-4sKtlz2O1cH5VqNQ6ca0+pii7pXmKgOM3wIDAQABo4ICnzCCApswDgYDVR0PAQH/BAQDAgAGMBIG
-A1UdEwEB/wQIMAYBAf8CAQQwEQYJYIZIAYb4QgEBBAQDAgAHMIICYAYJYIZIAYb4QgENBIICURaC
-Ak1GSUdZRUxFTSEgRXplbiB0YW51c2l0dmFueSBhIE5ldExvY2sgS2Z0LiBBbHRhbGFub3MgU3pv
-bGdhbHRhdGFzaSBGZWx0ZXRlbGVpYmVuIGxlaXJ0IGVsamFyYXNvayBhbGFwamFuIGtlc3p1bHQu
-IEEgaGl0ZWxlc2l0ZXMgZm9seWFtYXRhdCBhIE5ldExvY2sgS2Z0LiB0ZXJtZWtmZWxlbG9zc2Vn
-LWJpenRvc2l0YXNhIHZlZGkuIEEgZGlnaXRhbGlzIGFsYWlyYXMgZWxmb2dhZGFzYW5hayBmZWx0
-ZXRlbGUgYXogZWxvaXJ0IGVsbGVub3J6ZXNpIGVsamFyYXMgbWVndGV0ZWxlLiBBeiBlbGphcmFz
-IGxlaXJhc2EgbWVndGFsYWxoYXRvIGEgTmV0TG9jayBLZnQuIEludGVybmV0IGhvbmxhcGphbiBh
-IGh0dHBzOi8vd3d3Lm5ldGxvY2submV0L2RvY3MgY2ltZW4gdmFneSBrZXJoZXRvIGF6IGVsbGVu
-b3J6ZXNAbmV0bG9jay5uZXQgZS1tYWlsIGNpbWVuLiBJTVBPUlRBTlQhIFRoZSBpc3N1YW5jZSBh
-bmQgdGhlIHVzZSBvZiB0aGlzIGNlcnRpZmljYXRlIGlzIHN1YmplY3QgdG8gdGhlIE5ldExvY2sg
-Q1BTIGF2YWlsYWJsZSBhdCBodHRwczovL3d3dy5uZXRsb2NrLm5ldC9kb2NzIG9yIGJ5IGUtbWFp
-bCBhdCBjcHNAbmV0bG9jay5uZXQuMA0GCSqGSIb3DQEBBAUAA4IBAQBIJEb3ulZv+sgoA0BO5TE5
-ayZrU3/b39/zcT0mwBQOxmd7I6gMc90Bu8bKbjc5VdXHjFYgDigKDtIqpLBJUsY4B/6+CgmM0ZjP
-ytoUMaFP0jn8DxEsQ8Pdq5PHVT5HfBgaANzze9jyf1JsIPQLX2lS9O74silg6+NJMSEN1rUQQeJB
-CWziGppWS3cC9qCbmieH6FUpccKQn0V4GuEVZD3QDtigdp+uxdAu6tYPVuxkf1qbFFgBJ34TUMdr
-KuZoPL9coAob4Q566eKAw+np9v1sEZ7Q5SgnK1QyQhSCdeZK8CtmdWOMovsEPoMOmzbwGOQmIMOM
-8CgHrTwXZoi1/baI
------END CERTIFICATE-----
-
 XRamp Global CA Root
 ====================
 -----BEGIN CERTIFICATE-----
@@ -909,38 +662,6 @@ CZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDeLMDDav7v3Aun+kbfYNucpllQdSNpc5Oy
 +fwC00fmcc4QAu4njIT/rEUNE1yDMuAlpYYsfPQS
 -----END CERTIFICATE-----
 
-Swisscom Root CA 1
-==================
------BEGIN CERTIFICATE-----
-MIIF2TCCA8GgAwIBAgIQXAuFXAvnWUHfV8w/f52oNjANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQG
-EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
-dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMTAeFw0wNTA4MTgxMjA2MjBaFw0yNTA4
-MTgyMjA2MjBaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
-aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAxMIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0LmwqAzZuz8h+BvVM5OAFmUgdbI9m2BtRsiM
-MW8Xw/qabFbtPMWRV8PNq5ZJkCoZSx6jbVfd8StiKHVFXqrWW/oLJdihFvkcxC7mlSpnzNApbjyF
-NDhhSbEAn9Y6cV9Nbc5fuankiX9qUvrKm/LcqfmdmUc/TilftKaNXXsLmREDA/7n29uj/x2lzZAe
-AR81sH8A25Bvxn570e56eqeqDFdvpG3FEzuwpdntMhy0XmeLVNxzh+XTF3xmUHJd1BpYwdnP2IkC
-b6dJtDZd0KTeByy2dbcokdaXvij1mB7qWybJvbCXc9qukSbraMH5ORXWZ0sKbU/Lz7DkQnGMU3nn
-7uHbHaBuHYwadzVcFh4rUx80i9Fs/PJnB3r1re3WmquhsUvhzDdf/X/NTa64H5xD+SpYVUNFvJbN
-cA78yeNmuk6NO4HLFWR7uZToXTNShXEuT46iBhFRyePLoW4xCGQMwtI89Tbo19AOeCMgkckkKmUp
-WyL3Ic6DXqTz3kvTaI9GdVyDCW4pa8RwjPWd1yAv/0bSKzjCL3UcPX7ape8eYIVpQtPM+GP+HkM5
-haa2Y0EQs3MevNP6yn0WR+Kn1dCjigoIlmJWbjTb2QK5MHXjBNLnj8KwEUAKrNVxAmKLMb7dxiNY
-MUJDLXT5xp6mig/p/r+D5kNXJLrvRjSq1xIBOO0CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
-HQYDVR0hBBYwFDASBgdghXQBUwABBgdghXQBUwABMBIGA1UdEwEB/wQIMAYBAf8CAQcwHwYDVR0j
-BBgwFoAUAyUv3m+CATpcLNwroWm1Z9SM0/0wHQYDVR0OBBYEFAMlL95vggE6XCzcK6FptWfUjNP9
-MA0GCSqGSIb3DQEBBQUAA4ICAQA1EMvspgQNDQ/NwNurqPKIlwzfky9NfEBWMXrrpA9gzXrzvsMn
-jgM+pN0S734edAY8PzHyHHuRMSG08NBsl9Tpl7IkVh5WwzW9iAUPWxAaZOHHgjD5Mq2eUCzneAXQ
-MbFamIp1TpBcahQq4FJHgmDmHtqBsfsUC1rxn9KVuj7QG9YVHaO+htXbD8BJZLsuUBlL0iT43R4H
-VtA4oJVwIHaM190e3p9xxCPvgxNcoyQVTSlAPGrEqdi3pkSlDfTgnXceQHAm/NrZNuR55LU/vJtl
-vrsRls/bxig5OgjOR1tTWsWZ/l2p3e9M1MalrQLmjAcSHm8D0W+go/MpvRLHUKKwf4ipmXeascCl
-OS5cfGniLLDqN2qk4Vrh9VDlg++luyqI54zb/W1elxmofmZ1a3Hqv7HHb6D0jqTsNFFbjCYDcKF3
-1QESVwA12yPeDooomf2xEG9L/zgtYE4snOtnta1J7ksfrK/7DZBaZmBwXarNeNQk7shBoJMBkpxq
-nvy5JMWzFYJ+vq6VK+uxwNrjAWALXmmshFZhvnEX/h0TD/7Gh0Xp/jKgGg0TpJRVcaUWi7rKibCy
-x/yP2FS1k2Kdzs9Z+z0YzirLNRWCXf9UIltxUvu3yf5gmwBBZPCqKuy2QkPOiWaByIufOVQDJdMW
-NY6E0F/6MBr1mmz0DlP5OlvRHA==
------END CERTIFICATE-----
-
 DigiCert Assured ID Root CA
 ===========================
 -----BEGIN CERTIFICATE-----
@@ -1298,33 +1019,6 @@ wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHNpGxlaKFJdlxD
 ydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey
 -----END CERTIFICATE-----
 
-WellsSecure Public Root Certificate Authority
-=============================================
------BEGIN CERTIFICATE-----
-MIIEvTCCA6WgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoM
-F1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYw
-NAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcN
-MDcxMjEzMTcwNzU0WhcNMjIxMjE0MDAwNzU0WjCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dl
-bGxzIEZhcmdvIFdlbGxzU2VjdXJlMRwwGgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYD
-VQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDub7S9eeKPCCGeOARBJe+rWxxTkqxtnt3CxC5FlAM1
-iGd0V+PfjLindo8796jE2yljDpFoNoqXjopxaAkH5OjUDk/41itMpBb570OYj7OeUt9tkTmPOL13
-i0Nj67eT/DBMHAGTthP796EfvyXhdDcsHqRePGj4S78NuR4uNuip5Kf4D8uCdXw1LSLWwr8L87T8
-bJVhHlfXBIEyg1J55oNjz7fLY4sR4r1e6/aN7ZVyKLSsEmLpSjPmgzKuBXWVvYSV2ypcm44uDLiB
-K0HmOFafSZtsdvqKXfcBeYF8wYNABf5x/Qw/zE5gCQ5lRxAvAcAFP4/4s0HvWkJ+We/SlwxlAgMB
-AAGjggE0MIIBMDAPBgNVHRMBAf8EBTADAQH/MDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly9jcmwu
-cGtpLndlbGxzZmFyZ28uY29tL3dzcHJjYS5jcmwwDgYDVR0PAQH/BAQDAgHGMB0GA1UdDgQWBBQm
-lRkQ2eihl5H/3BnZtQQ+0nMKajCBsgYDVR0jBIGqMIGngBQmlRkQ2eihl5H/3BnZtQQ+0nMKaqGB
-i6SBiDCBhTELMAkGA1UEBhMCVVMxIDAeBgNVBAoMF1dlbGxzIEZhcmdvIFdlbGxzU2VjdXJlMRww
-GgYDVQQLDBNXZWxscyBGYXJnbyBCYW5rIE5BMTYwNAYDVQQDDC1XZWxsc1NlY3VyZSBQdWJsaWMg
-Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHmCAQEwDQYJKoZIhvcNAQEFBQADggEBALkVsUSRzCPI
-K0134/iaeycNzXK7mQDKfGYZUMbVmO2rvwNa5U3lHshPcZeG1eMd/ZDJPHV3V3p9+N701NX3leZ0
-bh08rnyd2wIDBSxxSyU+B+NemvVmFymIGjifz6pBA4SXa5M4esowRBskRDPQ5NHcKDj0E0M1NSlj
-qHyita04pO2t/caaH/+Xc/77szWnk4bGdpEA5qxRFsQnMlzbc9qlk1eOPm01JghZ1edE13YgY+es
-E2fDbbFwRnzVlhE9iW9dqKHrjQrawx0zbKPqZxmamX9LPYNRKh3KL4YMon4QLSvUFpULB6ouFJJJ
-tylv2G0xffX8oRAHh84vWdw+WNs=
------END CERTIFICATE-----
-
 COMODO ECC Certification Authority
 ==================================
 -----BEGIN CERTIFICATE-----
@@ -1342,30 +1036,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
 U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
 -----END CERTIFICATE-----
 
-IGC/A
-=====
------BEGIN CERTIFICATE-----
-MIIEAjCCAuqgAwIBAgIFORFFEJQwDQYJKoZIhvcNAQEFBQAwgYUxCzAJBgNVBAYTAkZSMQ8wDQYD
-VQQIEwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVE
-Q1NTSTEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZy
-MB4XDTAyMTIxMzE0MjkyM1oXDTIwMTAxNzE0MjkyMlowgYUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQI
-EwZGcmFuY2UxDjAMBgNVBAcTBVBhcmlzMRAwDgYDVQQKEwdQTS9TR0ROMQ4wDAYDVQQLEwVEQ1NT
-STEOMAwGA1UEAxMFSUdDL0ExIzAhBgkqhkiG9w0BCQEWFGlnY2FAc2dkbi5wbS5nb3V2LmZyMIIB
-IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsh/R0GLFMzvABIaIs9z4iPf930Pfeo2aSVz2
-TqrMHLmh6yeJ8kbpO0px1R2OLc/mratjUMdUC24SyZA2xtgv2pGqaMVy/hcKshd+ebUyiHDKcMCW
-So7kVc0dJ5S/znIq7Fz5cyD+vfcuiWe4u0dzEvfRNWk68gq5rv9GQkaiv6GFGvm/5P9JhfejcIYy
-HF2fYPepraX/z9E0+X1bF8bc1g4oa8Ld8fUzaJ1O/Id8NhLWo4DoQw1VYZTqZDdH6nfK0LJYBcNd
-frGoRpAxVs5wKpayMLh35nnAvSk7/ZR3TL0gzUEl4C7HG7vupARB0l2tEmqKm0f7yd1GQOGdPDPQ
-tQIDAQABo3cwdTAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBRjAVBgNVHSAEDjAMMAoGCCqB
-egF5AQEBMB0GA1UdDgQWBBSjBS8YYFDCiQrdKyFP/45OqDAxNjAfBgNVHSMEGDAWgBSjBS8YYFDC
-iQrdKyFP/45OqDAxNjANBgkqhkiG9w0BAQUFAAOCAQEABdwm2Pp3FURo/C9mOnTgXeQp/wYHE4RK
-q89toB9RlPhJy3Q2FLwV3duJL92PoF189RLrn544pEfMs5bZvpwlqwN+Mw+VgQ39FuCIvjfwbF3Q
-MZsyK10XZZOYYLxuj7GoPB7ZHPOpJkL5ZB3C55L29B5aqhlSXa/oovdgoPaN8In1buAKBQGVyYsg
-Crpa/JosPL3Dt8ldeCUFP1YUmwza+zpI/pdpXsoQhvdOlgQITeywvl3cO45Pwf2aNjSaTFR+FwNI
-lQgRHAdvhQh+XU3Endv7rs6y0bO4g2wdsrN58dhwmX7wEwLOXt1R0982gaEbeC9xs/FZTEYYKKuF
-0mBWWg==
------END CERTIFICATE-----
-
 Security Communication EV RootCA1
 =================================
 -----BEGIN CERTIFICATE-----
@@ -1410,46 +1080,6 @@ hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZiFj4A4xylNoEY
 okxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ/L7fCg0=
 -----END CERTIFICATE-----
 
-Microsec e-Szigno Root CA
-=========================
------BEGIN CERTIFICATE-----
-MIIHqDCCBpCgAwIBAgIRAMy4579OKRr9otxmpRwsDxEwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UE
-BhMCSFUxETAPBgNVBAcTCEJ1ZGFwZXN0MRYwFAYDVQQKEw1NaWNyb3NlYyBMdGQuMRQwEgYDVQQL
-EwtlLVN6aWdubyBDQTEiMCAGA1UEAxMZTWljcm9zZWMgZS1Temlnbm8gUm9vdCBDQTAeFw0wNTA0
-MDYxMjI4NDRaFw0xNzA0MDYxMjI4NDRaMHIxCzAJBgNVBAYTAkhVMREwDwYDVQQHEwhCdWRhcGVz
-dDEWMBQGA1UEChMNTWljcm9zZWMgTHRkLjEUMBIGA1UECxMLZS1Temlnbm8gQ0ExIjAgBgNVBAMT
-GU1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
-AQDtyADVgXvNOABHzNuEwSFpLHSQDCHZU4ftPkNEU6+r+ICbPHiN1I2uuO/TEdyB5s87lozWbxXG
-d36hL+BfkrYn13aaHUM86tnsL+4582pnS4uCzyL4ZVX+LMsvfUh6PXX5qqAnu3jCBspRwn5mS6/N
-oqdNAoI/gqyFxuEPkEeZlApxcpMqyabAvjxWTHOSJ/FrtfX9/DAFYJLG65Z+AZHCabEeHXtTRbjc
-QR/Ji3HWVBTji1R4P770Yjtb9aPs1ZJ04nQw7wHb4dSrmZsqa/i9phyGI0Jf7Enemotb9HI6QMVJ
-PqW+jqpx62z69Rrkav17fVVA71hu5tnVvCSrwe+3AgMBAAGjggQ3MIIEMzBnBggrBgEFBQcBAQRb
-MFkwKAYIKwYBBQUHMAGGHGh0dHBzOi8vcmNhLmUtc3ppZ25vLmh1L29jc3AwLQYIKwYBBQUHMAKG
-IWh0dHA6Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNydDAPBgNVHRMBAf8EBTADAQH/MIIBcwYD
-VR0gBIIBajCCAWYwggFiBgwrBgEEAYGoGAIBAQEwggFQMCgGCCsGAQUFBwIBFhxodHRwOi8vd3d3
-LmUtc3ppZ25vLmh1L1NaU1ovMIIBIgYIKwYBBQUHAgIwggEUHoIBEABBACAAdABhAG4A+gBzAO0A
-dAB2AOEAbgB5ACAA6QByAHQAZQBsAG0AZQB6AOkAcwDpAGgAZQB6ACAA6QBzACAAZQBsAGYAbwBn
-AGEAZADhAHMA4QBoAG8AegAgAGEAIABTAHoAbwBsAGcA4QBsAHQAYQB0APMAIABTAHoAbwBsAGcA
-4QBsAHQAYQB0AOEAcwBpACAAUwB6AGEAYgDhAGwAeQB6AGEAdABhACAAcwB6AGUAcgBpAG4AdAAg
-AGsAZQBsAGwAIABlAGwAagDhAHIAbgBpADoAIABoAHQAdABwADoALwAvAHcAdwB3AC4AZQAtAHMA
-egBpAGcAbgBvAC4AaAB1AC8AUwBaAFMAWgAvMIHIBgNVHR8EgcAwgb0wgbqggbeggbSGIWh0dHA6
-Ly93d3cuZS1zemlnbm8uaHUvUm9vdENBLmNybIaBjmxkYXA6Ly9sZGFwLmUtc3ppZ25vLmh1L0NO
-PU1pY3Jvc2VjJTIwZS1Temlnbm8lMjBSb290JTIwQ0EsT1U9ZS1Temlnbm8lMjBDQSxPPU1pY3Jv
-c2VjJTIwTHRkLixMPUJ1ZGFwZXN0LEM9SFU/Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5h
-cnkwDgYDVR0PAQH/BAQDAgEGMIGWBgNVHREEgY4wgYuBEGluZm9AZS1zemlnbm8uaHWkdzB1MSMw
-IQYDVQQDDBpNaWNyb3NlYyBlLVN6aWduw7MgUm9vdCBDQTEWMBQGA1UECwwNZS1TemlnbsOzIEhT
-WjEWMBQGA1UEChMNTWljcm9zZWMgS2Z0LjERMA8GA1UEBxMIQnVkYXBlc3QxCzAJBgNVBAYTAkhV
-MIGsBgNVHSMEgaQwgaGAFMegSXUWYYTbMUuE0vE3QJDvTtz3oXakdDByMQswCQYDVQQGEwJIVTER
-MA8GA1UEBxMIQnVkYXBlc3QxFjAUBgNVBAoTDU1pY3Jvc2VjIEx0ZC4xFDASBgNVBAsTC2UtU3pp
-Z25vIENBMSIwIAYDVQQDExlNaWNyb3NlYyBlLVN6aWdubyBSb290IENBghEAzLjnv04pGv2i3Gal
-HCwPETAdBgNVHQ4EFgQUx6BJdRZhhNsxS4TS8TdAkO9O3PcwDQYJKoZIhvcNAQEFBQADggEBANMT
-nGZjWS7KXHAM/IO8VbH0jgdsZifOwTsgqRy7RlRw7lrMoHfqaEQn6/Ip3Xep1fvj1KcExJW4C+FE
-aGAHQzAxQmHl7tnlJNUb3+FKG6qfx1/4ehHqE5MAyopYse7tDk2016g2JnzgOsHVV4Lxdbb9iV/a
-86g4nzUGCM4ilb7N1fy+W955a9x6qWVmvrElWl/tftOsRm1M9DKHtCAE4Gx4sHfRhUZLphK3dehK
-yVZs15KrnfVJONJPU+NVkBHbmJbGSfI+9J8b4PeI3CVimUTYc78/MPMMNz7UwiiAc7EBt51alhQB
-S6kRnSlqLtBdgcDPsiBDxwPgN05dCtxZICU=
------END CERTIFICATE-----
-
 Certigna
 ========
 -----BEGIN CERTIFICATE-----
@@ -1575,58 +1205,6 @@ LBOhgLJeDEoTniDYYkCrkOpkSi+sDQESeUWoL4cZaMjihccwsnX5OD+ywJO0a+IDRM5noN+J1q2M
 dqMTw5RhK2vZbMEHCiIHhWyFJEapvj+LeISCfiQMnf2BN+MlqO02TpUsyZyQ2uypQjyttgI=
 -----END CERTIFICATE-----
 
-Buypass Class 2 CA 1
-====================
------BEGIN CERTIFICATE-----
-MIIDUzCCAjugAwIBAgIBATANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU
-QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMiBDQSAxMB4XDTA2
-MTAxMzEwMjUwOVoXDTE2MTAxMzEwMjUwOVowSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh
-c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDIgQ0EgMTCCASIwDQYJKoZI
-hvcNAQEBBQADggEPADCCAQoCggEBAIs8B0XY9t/mx8q6jUPFR42wWsE425KEHK8T1A9vNkYgxC7M
-cXA0ojTTNy7Y3Tp3L8DrKehc0rWpkTSHIln+zNvnma+WwajHQN2lFYxuyHyXA8vmIPLXl18xoS83
-0r7uvqmtqEyeIWZDO6i88wmjONVZJMHCR3axiFyCO7srpgTXjAePzdVBHfCuuCkslFJgNJQ72uA4
-0Z0zPhX0kzLFANq1KWYOOngPIVJfAuWSeyXTkh4vFZ2B5J2O6O+JzhRMVB0cgRJNcKi+EAUXfh/R
-uFdV7c27UsKwHnjCTTZoy1YmwVLBvXb3WNVyfh9EdrsAiR0WnVE1703CVu9r4Iw7DekCAwEAAaNC
-MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUP42aWYv8e3uco684sDntkHGA1sgwDgYDVR0P
-AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAVGn4TirnoB6NLJzKyQJHyIdFkhb5jatLPgcIV
-1Xp+DCmsNx4cfHZSldq1fyOhKXdlyTKdqC5Wq2B2zha0jX94wNWZUYN/Xtm+DKhQ7SLHrQVMdvvt
-7h5HZPb3J31cKA9FxVxiXqaakZG3Uxcu3K1gnZZkOb1naLKuBctN518fV4bVIJwo+28TOPX2EZL2
-fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w
-wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho
------END CERTIFICATE-----
-
-EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1
-==========================================================================
------BEGIN CERTIFICATE-----
-MIIF5zCCA8+gAwIBAgIITK9zQhyOdAIwDQYJKoZIhvcNAQEFBQAwgYAxODA2BgNVBAMML0VCRyBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMTcwNQYDVQQKDC5FQkcg
-QmlsacWfaW0gVGVrbm9sb2ppbGVyaSB2ZSBIaXptZXRsZXJpIEEuxZ4uMQswCQYDVQQGEwJUUjAe
-Fw0wNjA4MTcwMDIxMDlaFw0xNjA4MTQwMDMxMDlaMIGAMTgwNgYDVQQDDC9FQkcgRWxla3Ryb25p
-ayBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsTE3MDUGA1UECgwuRUJHIEJpbGnFn2lt
-IFRla25vbG9qaWxlcmkgdmUgSGl6bWV0bGVyaSBBLsWeLjELMAkGA1UEBhMCVFIwggIiMA0GCSqG
-SIb3DQEBAQUAA4ICDwAwggIKAoICAQDuoIRh0DpqZhAy2DE4f6en5f2h4fuXd7hxlugTlkaDT7by
-X3JWbhNgpQGR4lvFzVcfd2NR/y8927k/qqk153nQ9dAktiHq6yOU/im/+4mRDGSaBUorzAzu8T2b
-gmmkTPiab+ci2hC6X5L8GCcKqKpE+i4stPtGmggDg3KriORqcsnlZR9uKg+ds+g75AxuetpX/dfr
-eYteIAbTdgtsApWjluTLdlHRKJ2hGvxEok3MenaoDT2/F08iiFD9rrbskFBKW5+VQarKD7JK/oCZ
-TqNGFav4c0JqwmZ2sQomFd2TkuzbqV9UIlKRcF0T6kjsbgNs2d1s/OsNA/+mgxKb8amTD8UmTDGy
-Y5lhcucqZJnSuOl14nypqZoaqsNW2xCaPINStnuWt6yHd6i58mcLlEOzrz5z+kI2sSXFCjEmN1Zn
-uqMLfdb3ic1nobc6HmZP9qBVFCVMLDMNpkGMvQQxahByCp0OLna9XvNRiYuoP1Vzv9s6xiQFlpJI
-qkuNKgPlV5EQ9GooFW5Hd4RcUXSfGenmHmMWOeMRFeNYGkS9y8RsZteEBt8w9DeiQyJ50hBs37vm
-ExH8nYQKE3vwO9D8owrXieqWfo1IhR5kX9tUoqzVegJ5a9KK8GfaZXINFHDk6Y54jzJ0fFfy1tb0
-Nokb+Clsi7n2l9GkLqq+CxnCRelwXQIDAJ3Zo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB
-/wQEAwIBBjAdBgNVHQ4EFgQU587GT/wWZ5b6SqMHwQSny2re2kcwHwYDVR0jBBgwFoAU587GT/wW
-Z5b6SqMHwQSny2re2kcwDQYJKoZIhvcNAQEFBQADggIBAJuYml2+8ygjdsZs93/mQJ7ANtyVDR2t
-FcU22NU57/IeIl6zgrRdu0waypIN30ckHrMk2pGI6YNw3ZPX6bqz3xZaPt7gyPvT/Wwp+BVGoGgm
-zJNSroIBk5DKd8pNSe/iWtkqvTDOTLKBtjDOWU/aWR1qeqRFsIImgYZ29fUQALjuswnoT4cCB64k
-XPBfrAowzIpAoHMEwfuJJPaaHFy3PApnNgUIMbOv2AFoKuB4j3TeuFGkjGwgPaL7s9QJ/XvCgKqT
-bCmYIai7FvOpEl90tYeY8pUm3zTvilORiF0alKM/fCL414i6poyWqD1SNGKfAB5UVUJnxk1Gj7sU
-RT0KlhaOEKGXmdXTMIXM3rRyt7yKPBgpaP3ccQfuJDlq+u2lrDgv+R4QDgZxGhBM/nV+/x5XOULK
-1+EVoVZVWRvRo68R2E7DpSvvkL/A7IITW43WciyTTo9qKd+FPNMN4KIYEsxVL0e3p5sC/kH2iExt
-2qkBR4NkJ2IQgtYSe14DHzSpyZH+r11thie3I6p1GMog57AP14kOpmciY/SDQSsGS7tY1dHXt7kQ
-Y9iJSrSq3RZj9W6+YKH47ejWkE8axsWgKdOnIaj1Wjz3x0miIZpKlVIglnKaZsv30oZDfCK+lvm9
-AahH3eU7QPl1K5srRmSGjR70j/sHd9DqSaIcjVIUpgqT
------END CERTIFICATE-----
-
 certSIGN ROOT CA
 ================
 -----BEGIN CERTIFICATE-----
@@ -1647,49 +1225,6 @@ vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz
 TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD
 -----END CERTIFICATE-----
 
-CNNIC ROOT
-==========
------BEGIN CERTIFICATE-----
-MIIDVTCCAj2gAwIBAgIESTMAATANBgkqhkiG9w0BAQUFADAyMQswCQYDVQQGEwJDTjEOMAwGA1UE
-ChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1QwHhcNMDcwNDE2MDcwOTE0WhcNMjcwNDE2MDcw
-OTE0WjAyMQswCQYDVQQGEwJDTjEOMAwGA1UEChMFQ05OSUMxEzARBgNVBAMTCkNOTklDIFJPT1Qw
-ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTNfc/c3et6FtzF8LRb+1VvG7q6KR5smzD
-o+/hn7E7SIX1mlwhIhAsxYLO2uOabjfhhyzcuQxauohV3/2q2x8x6gHx3zkBwRP9SFIhxFXf2tiz
-VHa6dLG3fdfA6PZZxU3Iva0fFNrfWEQlMhkqx35+jq44sDB7R3IJMfAw28Mbdim7aXZOV/kbZKKT
-VrdvmW7bCgScEeOAH8tjlBAKqeFkgjH5jCftppkA9nCTGPihNIaj3XrCGHn2emU1z5DrvTOTn1Or
-czvmmzQgLx3vqR1jGqCA2wMv+SYahtKNu6m+UjqHZ0gNv7Sg2Ca+I19zN38m5pIEo3/PIKe38zrK
-y5nLAgMBAAGjczBxMBEGCWCGSAGG+EIBAQQEAwIABzAfBgNVHSMEGDAWgBRl8jGtKvf33VKWCscC
-wQ7vptU7ETAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIB/jAdBgNVHQ4EFgQUZfIxrSr3991S
-lgrHAsEO76bVOxEwDQYJKoZIhvcNAQEFBQADggEBAEs17szkrr/Dbq2flTtLP1se31cpolnKOOK5
-Gv+e5m4y3R6u6jW39ZORTtpC4cMXYFDy0VwmuYK36m3knITnA3kXr5g9lNvHugDnuL8BV8F3RTIM
-O/G0HAiw/VGgod2aHRM2mm23xzy54cXZF/qD1T0VoDy7HgviyJA/qIYM/PmLXoXLT1tLYhFHxUV8
-BS9BsZ4QaRuZluBVeftOhpm4lNqGOGqTo+fLbuXf6iFViZx9fX+Y9QCJ7uOEwFyWtcVG6kbghVW2
-G8kS1sHNzYDzAgE8yGnLRUhj2JTQ7IUOO04RZfSCjKY9ri4ilAnIXOo8gV0WKgOXFlUJ24pBgp5m
-mxE=
------END CERTIFICATE-----
-
-ApplicationCA - Japanese Government
-===================================
------BEGIN CERTIFICATE-----
-MIIDoDCCAoigAwIBAgIBMTANBgkqhkiG9w0BAQUFADBDMQswCQYDVQQGEwJKUDEcMBoGA1UEChMT
-SmFwYW5lc2UgR292ZXJubWVudDEWMBQGA1UECxMNQXBwbGljYXRpb25DQTAeFw0wNzEyMTIxNTAw
-MDBaFw0xNzEyMTIxNTAwMDBaMEMxCzAJBgNVBAYTAkpQMRwwGgYDVQQKExNKYXBhbmVzZSBHb3Zl
-cm5tZW50MRYwFAYDVQQLEw1BcHBsaWNhdGlvbkNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEAp23gdE6Hj6UG3mii24aZS2QNcfAKBZuOquHMLtJqO8F6tJdhjYq+xpqcBrSGUeQ3DnR4
-fl+Kf5Sk10cI/VBaVuRorChzoHvpfxiSQE8tnfWuREhzNgaeZCw7NCPbXCbkcXmP1G55IrmTwcrN
-wVbtiGrXoDkhBFcsovW8R0FPXjQilbUfKW1eSvNNcr5BViCH/OlQR9cwFO5cjFW6WY2H/CPek9AE
-jP3vbb3QesmlOmpyM8ZKDQUXKi17safY1vC+9D/qDihtQWEjdnjDuGWk81quzMKq2edY3rZ+nYVu
-nyoKb58DKTCXKB28t89UKU5RMfkntigm/qJj5kEW8DOYRwIDAQABo4GeMIGbMB0GA1UdDgQWBBRU
-WssmP3HMlEYNllPqa0jQk/5CdTAOBgNVHQ8BAf8EBAMCAQYwWQYDVR0RBFIwUKROMEwxCzAJBgNV
-BAYTAkpQMRgwFgYDVQQKDA/ml6XmnKzlm73mlL/lupwxIzAhBgNVBAsMGuOCouODl+ODquOCseOD
-vOOCt+ODp+ODs0NBMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBADlqRHZ3ODrs
-o2dGD/mLBqj7apAxzn7s2tGJfHrrLgy9mTLnsCTWw//1sogJhyzjVOGjprIIC8CFqMjSnHH2HZ9g
-/DgzE+Ge3Atf2hZQKXsvcJEPmbo0NI2VdMV+eKlmXb3KIXdCEKxmJj3ekav9FfBv7WxfEPjzFvYD
-io+nEhEMy/0/ecGc/WLuo89UDNErXxc+4z6/wCs+CZv+iKZ+tJIX/COUgb1up8WMwusRRdv4QcmW
-dupwX3kSa+SjB1oF7ydJzyGfikwJcGapJsErEU4z0g781mzSDjJkaP+tBXhfAx2o45CsJOAPQKdL
-rosot4LKGAfmt1t06SAZf7IbiVQ=
------END CERTIFICATE-----
-
 GeoTrust Primary Certification Authority - G3
 =============================================
 -----BEGIN CERTIFICATE-----
@@ -1821,7 +1356,7 @@ AJw9SDkjOVgaFRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA==
 -----END CERTIFICATE-----
 
 NetLock Arany (Class Gold) Főtanúsítvány
-============================================
+========================================
 -----BEGIN CERTIFICATE-----
 MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G
 A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610
@@ -1876,58 +1411,6 @@ IPVVYpbtbZNQvOSqeK3Zywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm
 66+KAQ==
 -----END CERTIFICATE-----
 
-CA Disig
-========
------BEGIN CERTIFICATE-----
-MIIEDzCCAvegAwIBAgIBATANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMK
-QnJhdGlzbGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwHhcNMDYw
-MzIyMDEzOTM0WhcNMTYwMzIyMDEzOTM0WjBKMQswCQYDVQQGEwJTSzETMBEGA1UEBxMKQnJhdGlz
-bGF2YTETMBEGA1UEChMKRGlzaWcgYS5zLjERMA8GA1UEAxMIQ0EgRGlzaWcwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQCS9jHBfYj9mQGp2HvycXXxMcbzdWb6UShGhJd4NLxs/LxFWYgm
-GErENx+hSkS943EE9UQX4j/8SFhvXJ56CbpRNyIjZkMhsDxkovhqFQ4/61HhVKndBpnXmjxUizkD
-Pw/Fzsbrg3ICqB9x8y34dQjbYkzo+s7552oftms1grrijxaSfQUMbEYDXcDtab86wYqg6I7ZuUUo
-hwjstMoVvoLdtUSLLa2GDGhibYVW8qwUYzrG0ZmsNHhWS8+2rT+MitcE5eN4TPWGqvWP+j1scaMt
-ymfraHtuM6kMgiioTGohQBUgDCZbg8KpFhXAJIJdKxatymP2dACw30PEEGBWZ2NFAgMBAAGjgf8w
-gfwwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUjbJJaJ1yCCW5wCf1UJNWSEZx+Y8wDgYDVR0P
-AQH/BAQDAgEGMDYGA1UdEQQvMC2BE2Nhb3BlcmF0b3JAZGlzaWcuc2uGFmh0dHA6Ly93d3cuZGlz
-aWcuc2svY2EwZgYDVR0fBF8wXTAtoCugKYYnaHR0cDovL3d3dy5kaXNpZy5zay9jYS9jcmwvY2Ff
-ZGlzaWcuY3JsMCygKqAohiZodHRwOi8vY2EuZGlzaWcuc2svY2EvY3JsL2NhX2Rpc2lnLmNybDAa
-BgNVHSAEEzARMA8GDSuBHpGT5goAAAABAQEwDQYJKoZIhvcNAQEFBQADggEBAF00dGFMrzvY/59t
-WDYcPQuBDRIrRhCA/ec8J9B6yKm2fnQwM6M6int0wHl5QpNt/7EpFIKrIYwvF/k/Ji/1WcbvgAa3
-mkkp7M5+cTxqEEHA9tOasnxakZzArFvITV734VP/Q3f8nktnbNfzg9Gg4H8l37iYC5oyOGwwoPP/
-CBUz91BKez6jPiCp3C9WgArtQVCwyfTssuMmRAAOb54GvCKWU3BlxFAKRmukLyeBEicTXxChds6K
-ezfqwzlhA5WYOudsiCUI/HloDYd9Yvi0X/vF2Ey9WLw/Q1vUHgFNPGO+I++MzVpQuGhU+QqZMxEA
-4Z7CRneC9VkGjCFMhwnN5ag=
------END CERTIFICATE-----
-
-Juur-SK
-=======
------BEGIN CERTIFICATE-----
-MIIE5jCCA86gAwIBAgIEO45L/DANBgkqhkiG9w0BAQUFADBdMRgwFgYJKoZIhvcNAQkBFglwa2lA
-c2suZWUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKExlBUyBTZXJ0aWZpdHNlZXJpbWlza2Vza3VzMRAw
-DgYDVQQDEwdKdXVyLVNLMB4XDTAxMDgzMDE0MjMwMVoXDTE2MDgyNjE0MjMwMVowXTEYMBYGCSqG
-SIb3DQEJARYJcGtpQHNrLmVlMQswCQYDVQQGEwJFRTEiMCAGA1UEChMZQVMgU2VydGlmaXRzZWVy
-aW1pc2tlc2t1czEQMA4GA1UEAxMHSnV1ci1TSzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
-ggEBAIFxNj4zB9bjMI0TfncyRsvPGbJgMUaXhvSYRqTCZUXP00B841oiqBB4M8yIsdOBSvZiF3tf
-TQou0M+LI+5PAk676w7KvRhj6IAcjeEcjT3g/1tf6mTll+g/mX8MCgkzABpTpyHhOEvWgxutr2TC
-+Rx6jGZITWYfGAriPrsfB2WThbkasLnE+w0R9vXW+RvHLCu3GFH+4Hv2qEivbDtPL+/40UceJlfw
-UR0zlv/vWT3aTdEVNMfqPxZIe5EcgEMPPbgFPtGzlc3Yyg/CQ2fbt5PgIoIuvvVoKIO5wTtpeyDa
-Tpxt4brNj3pssAki14sL2xzVWiZbDcDq5WDQn/413z8CAwEAAaOCAawwggGoMA8GA1UdEwEB/wQF
-MAMBAf8wggEWBgNVHSAEggENMIIBCTCCAQUGCisGAQQBzh8BAQEwgfYwgdAGCCsGAQUFBwICMIHD
-HoHAAFMAZQBlACAAcwBlAHIAdABpAGYAaQBrAGEAYQB0ACAAbwBuACAAdgDkAGwAagBhAHMAdABh
-AHQAdQBkACAAQQBTAC0AaQBzACAAUwBlAHIAdABpAGYAaQB0AHMAZQBlAHIAaQBtAGkAcwBrAGUA
-cwBrAHUAcwAgAGEAbABhAG0ALQBTAEsAIABzAGUAcgB0AGkAZgBpAGsAYQBhAHQAaQBkAGUAIABr
-AGkAbgBuAGkAdABhAG0AaQBzAGUAawBzMCEGCCsGAQUFBwIBFhVodHRwOi8vd3d3LnNrLmVlL2Nw
-cy8wKwYDVR0fBCQwIjAgoB6gHIYaaHR0cDovL3d3dy5zay5lZS9qdXVyL2NybC8wHQYDVR0OBBYE
-FASqekej5ImvGs8KQKcYP2/v6X2+MB8GA1UdIwQYMBaAFASqekej5ImvGs8KQKcYP2/v6X2+MA4G
-A1UdDwEB/wQEAwIB5jANBgkqhkiG9w0BAQUFAAOCAQEAe8EYlFOiCfP+JmeaUOTDBS8rNXiRTHyo
-ERF5TElZrMj3hWVcRrs7EKACr81Ptcw2Kuxd/u+gkcm2k298gFTsxwhwDY77guwqYHhpNjbRxZyL
-abVAyJRld/JXIWY7zoVAtjNjGr95HvxcHdMdkxuLDF2FvZkwMhgJkVLpfKG6/2SSmuz+Ne6ML678
-IIbsSt4beDI3poHSna9aEhbKmVv8b20OxaAehsmR0FyYgl9jDIpaq9iVpszLita/ZEuOyoqysOkh
-Mp6qqIWYNIE5ITuoOlIyPfZrN4YGWhWY3PARZv40ILcD9EEQfTmEeZZyY7aWAuVrua0ZTbvGRNs2
-yyqcjg==
------END CERTIFICATE-----
-
 Hongkong Post Root CA 1
 =======================
 -----BEGIN CERTIFICATE-----
@@ -2361,7 +1844,7 @@ Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI
 -----END CERTIFICATE-----
 
 Certinomis - Autorité Racine
-=============================
+============================
 -----BEGIN CERTIFICATE-----
 MIIFnDCCA4SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJGUjETMBEGA1UEChMK
 Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxJjAkBgNVBAMMHUNlcnRpbm9taXMg
@@ -2391,41 +1874,6 @@ wk01+dIL8hf2rGbVJLJP0RyZwG71fet0BLj5TXcJ17TPBzAJ8bgAVtkXFhYKK4bfjwEZGuW7gmP/
 vgt2Fl43N+bYdJeimUV5
 -----END CERTIFICATE-----
 
-Root CA Generalitat Valenciana
-==============================
------BEGIN CERTIFICATE-----
-MIIGizCCBXOgAwIBAgIEO0XlaDANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJFUzEfMB0GA1UE
-ChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290
-IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwHhcNMDEwNzA2MTYyMjQ3WhcNMjEwNzAxMTUyMjQ3
-WjBoMQswCQYDVQQGEwJFUzEfMB0GA1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UE
-CxMGUEtJR1ZBMScwJQYDVQQDEx5Sb290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmEwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGKqtXETcvIorKA3Qdyu0togu8M1JAJke+WmmmO3I2
-F0zo37i7L3bhQEZ0ZQKQUgi0/6iMweDHiVYQOTPvaLRfX9ptI6GJXiKjSgbwJ/BXufjpTjJ3Cj9B
-ZPPrZe52/lSqfR0grvPXdMIKX/UIKFIIzFVd0g/bmoGlu6GzwZTNVOAydTGRGmKy3nXiz0+J2ZGQ
-D0EbtFpKd71ng+CT516nDOeB0/RSrFOyA8dEJvt55cs0YFAQexvba9dHq198aMpunUEDEO5rmXte
-JajCq+TA81yc477OMUxkHl6AovWDfgzWyoxVjr7gvkkHD6MkQXpYHYTqWBLI4bft75PelAgxAgMB
-AAGjggM7MIIDNzAyBggrBgEFBQcBAQQmMCQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnBraS5n
-dmEuZXMwEgYDVR0TAQH/BAgwBgEB/wIBAjCCAjQGA1UdIASCAiswggInMIICIwYKKwYBBAG/VQIB
-ADCCAhMwggHoBggrBgEFBQcCAjCCAdoeggHWAEEAdQB0AG8AcgBpAGQAYQBkACAAZABlACAAQwBl
-AHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAFIAYQDtAHoAIABkAGUAIABsAGEAIABHAGUAbgBlAHIA
-YQBsAGkAdABhAHQAIABWAGEAbABlAG4AYwBpAGEAbgBhAC4ADQAKAEwAYQAgAEQAZQBjAGwAYQBy
-AGEAYwBpAPMAbgAgAGQAZQAgAFAAcgDhAGMAdABpAGMAYQBzACAAZABlACAAQwBlAHIAdABpAGYA
-aQBjAGEAYwBpAPMAbgAgAHEAdQBlACAAcgBpAGcAZQAgAGUAbAAgAGYAdQBuAGMAaQBvAG4AYQBt
-AGkAZQBuAHQAbwAgAGQAZQAgAGwAYQAgAHAAcgBlAHMAZQBuAHQAZQAgAEEAdQB0AG8AcgBpAGQA
-YQBkACAAZABlACAAQwBlAHIAdABpAGYAaQBjAGEAYwBpAPMAbgAgAHMAZQAgAGUAbgBjAHUAZQBu
-AHQAcgBhACAAZQBuACAAbABhACAAZABpAHIAZQBjAGMAaQDzAG4AIAB3AGUAYgAgAGgAdAB0AHAA
-OgAvAC8AdwB3AHcALgBwAGsAaQAuAGcAdgBhAC4AZQBzAC8AYwBwAHMwJQYIKwYBBQUHAgEWGWh0
-dHA6Ly93d3cucGtpLmd2YS5lcy9jcHMwHQYDVR0OBBYEFHs100DSHHgZZu90ECjcPk+yeAT8MIGV
-BgNVHSMEgY0wgYqAFHs100DSHHgZZu90ECjcPk+yeAT8oWykajBoMQswCQYDVQQGEwJFUzEfMB0G
-A1UEChMWR2VuZXJhbGl0YXQgVmFsZW5jaWFuYTEPMA0GA1UECxMGUEtJR1ZBMScwJQYDVQQDEx5S
-b290IENBIEdlbmVyYWxpdGF0IFZhbGVuY2lhbmGCBDtF5WgwDQYJKoZIhvcNAQEFBQADggEBACRh
-TvW1yEICKrNcda3FbcrnlD+laJWIwVTAEGmiEi8YPyVQqHxK6sYJ2fR1xkDar1CdPaUWu20xxsdz
-Ckj+IHLtb8zog2EWRpABlUt9jppSCS/2bxzkoXHPjCpaF3ODR00PNvsETUlR4hTJZGH71BTg9J63
-NI8KJr2XXPR5OkowGcytT6CYirQxlyric21+eLj4iIlPsSKRZEv1UN4D2+XFducTZnV+ZfsBn5OH
-iJ35Rld8TWCvmHMTI6QgkYH60GFmuH3Rr9ZvHmw96RH9qfmCIoaZM3Fa6hlXPZHNqcCjbgcTpsnt
-+GijnsNacgmHKNHEc8RzGF9QdRYxn7fofMM=
------END CERTIFICATE-----
-
 TWCA Root Certification Authority
 =================================
 -----BEGIN CERTIFICATE-----
@@ -2871,93 +2319,6 @@ poLWccret9W6aAjtmcz9opLLabid+Qqkpj5PkygqYWwHJgD/ll9ohri4zspV4KuxPX+Y1zMOWj3Y
 eMLEYC/HYvBhkdI4sPaeVdtAgAUSM84dkpvRabP/v/GSCmE1P93+hvS84Bpxs2Km
 -----END CERTIFICATE-----
 
-China Internet Network Information Center EV Certificates Root
-==============================================================
------BEGIN CERTIFICATE-----
-MIID9zCCAt+gAwIBAgIESJ8AATANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCQ04xMjAwBgNV
-BAoMKUNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyMUcwRQYDVQQDDD5D
-aGluYSBJbnRlcm5ldCBOZXR3b3JrIEluZm9ybWF0aW9uIENlbnRlciBFViBDZXJ0aWZpY2F0ZXMg
-Um9vdDAeFw0xMDA4MzEwNzExMjVaFw0zMDA4MzEwNzExMjVaMIGKMQswCQYDVQQGEwJDTjEyMDAG
-A1UECgwpQ2hpbmEgSW50ZXJuZXQgTmV0d29yayBJbmZvcm1hdGlvbiBDZW50ZXIxRzBFBgNVBAMM
-PkNoaW5hIEludGVybmV0IE5ldHdvcmsgSW5mb3JtYXRpb24gQ2VudGVyIEVWIENlcnRpZmljYXRl
-cyBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm35z7r07eKpkQ0H1UN+U8i6y
-jUqORlTSIRLIOTJCBumD1Z9S7eVnAztUwYyZmczpwA//DdmEEbK40ctb3B75aDFk4Zv6dOtouSCV
-98YPjUesWgbdYavi7NifFy2cyjw1l1VxzUOFsUcW9SxTgHbP0wBkvUCZ3czY28Sf1hNfQYOL+Q2H
-klY0bBoQCxfVWhyXWIQ8hBouXJE0bhlffxdpxWXvayHG1VA6v2G5BY3vbzQ6sm8UY78WO5upKv23
-KzhmBsUs4qpnHkWnjQRmQvaPK++IIGmPMowUc9orhpFjIpryp9vOiYurXccUwVswah+xt54ugQEC
-7c+WXmPbqOY4twIDAQABo2MwYTAfBgNVHSMEGDAWgBR8cks5x8DbYqVPm6oYNJKiyoOCWTAPBgNV
-HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUfHJLOcfA22KlT5uqGDSSosqD
-glkwDQYJKoZIhvcNAQEFBQADggEBACrDx0M3j92tpLIM7twUbY8opJhJywyA6vPtI2Z1fcXTIWd5
-0XPFtQO3WKwMVC/GVhMPMdoG52U7HW8228gd+f2ABsqjPWYWqJ1MFn3AlUa1UeTiH9fqBk1jjZaM
-7+czV0I664zBechNdn3e9rG3geCg+aF4RhcaVpjwTj2rHO3sOdwHSPdj/gauwqRcalsyiMXHM4Ws
-ZkJHwlgkmeHlPuV1LI5D1l08eB6olYIpUNHRFrrvwb562bTYzB5MRuF3sTGrvSrIzo9uoV1/A3U0
-5K2JRVRevq4opbs/eHnrc7MKDf2+yfdWrPa37S+bISnHOLaVxATywy39FCqQmbkHzJ8=
------END CERTIFICATE-----
-
-Swisscom Root CA 2
-==================
------BEGIN CERTIFICATE-----
-MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBkMQswCQYDVQQG
-EwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0YWwgQ2VydGlmaWNhdGUgU2Vy
-dmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3QgQ0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2
-MjUwNzM4MTRaMGQxCzAJBgNVBAYTAmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGln
-aXRhbCBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIIC
-IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvErjw0DzpPM
-LgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r0rk0X2s682Q2zsKwzxNo
-ysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJ
-wDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVPACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpH
-Wrumnf2U5NGKpV+GY3aFy6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1a
-SgJA/MTAtukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL6yxS
-NLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0uPoTXGiTOmekl9Ab
-mbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrALacywlKinh/LTSlDcX3KwFnUey7QY
-Ypqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velhk6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3
-qPyZ7iVNTA6z00yPhOgpD/0QVAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYw
-HQYDVR0hBBYwFDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O
-BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqhb97iEoHF8Twu
-MA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4RfbgZPnm3qKhyN2abGu2sEzsO
-v2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ
-82YqZh6NM4OKb3xuqFp1mrjX2lhIREeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLz
-o9v/tdhZsnPdTSpxsrpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcs
-a0vvaGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciATwoCqISxx
-OQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99nBjx8Oto0QuFmtEYE3saW
-mA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5Wt6NlUe07qxS/TFED6F+KBZvuim6c779o
-+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TC
-rvJcwhbtkj6EPnNgiLx29CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX
-5OfNeOI5wSsSnqaeG8XmDtkx2Q==
------END CERTIFICATE-----
-
-Swisscom Root EV CA 2
-=====================
------BEGIN CERTIFICATE-----
-MIIF4DCCA8igAwIBAgIRAPL6ZOJ0Y9ON/RAdBB92ylgwDQYJKoZIhvcNAQELBQAwZzELMAkGA1UE
-BhMCY2gxETAPBgNVBAoTCFN3aXNzY29tMSUwIwYDVQQLExxEaWdpdGFsIENlcnRpZmljYXRlIFNl
-cnZpY2VzMR4wHAYDVQQDExVTd2lzc2NvbSBSb290IEVWIENBIDIwHhcNMTEwNjI0MDk0NTA4WhcN
-MzEwNjI1MDg0NTA4WjBnMQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsT
-HERpZ2l0YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxHjAcBgNVBAMTFVN3aXNzY29tIFJvb3QgRVYg
-Q0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMT3HS9X6lds93BdY7BxUglgRCgz
-o3pOCvrY6myLURYaVa5UJsTMRQdBTxB5f3HSek4/OE6zAMaVylvNwSqD1ycfMQ4jFrclyxy0uYAy
-Xhqdk/HoPGAsp15XGVhRXrwsVgu42O+LgrQ8uMIkqBPHoCE2G3pXKSinLr9xJZDzRINpUKTk4Rti
-GZQJo/PDvO/0vezbE53PnUgJUmfANykRHvvSEaeFGHR55E+FFOtSN+KxRdjMDUN/rhPSays/p8Li
-qG12W0OfvrSdsyaGOx9/5fLoZigWJdBLlzin5M8J0TbDC77aO0RYjb7xnglrPvMyxyuHxuxenPaH
-Za0zKcQvidm5y8kDnftslFGXEBuGCxobP/YCfnvUxVFkKJ3106yDgYjTdLRZncHrYTNaRdHLOdAG
-alNgHa/2+2m8atwBz735j9m9W8E6X47aD0upm50qKGsaCnw8qyIL5XctcfaCNYGu+HuB5ur+rPQa
-m3Rc6I8k9l2dRsQs0h4rIWqDJ2dVSqTjyDKXZpBy2uPUZC5f46Fq9mDU5zXNysRojddxyNMkM3Ox
-bPlq4SjbX8Y96L5V5jcb7STZDxmPX2MYWFCBUWVv8p9+agTnNCRxunZLWB4ZvRVgRaoMEkABnRDi
-xzgHcgplwLa7JSnaFp6LNYth7eVxV4O1PHGf40+/fh6Bn0GXAgMBAAGjgYYwgYMwDgYDVR0PAQH/
-BAQDAgGGMB0GA1UdIQQWMBQwEgYHYIV0AVMCAgYHYIV0AVMCAjASBgNVHRMBAf8ECDAGAQH/AgED
-MB0GA1UdDgQWBBRF2aWBbj2ITY1x0kbBbkUe88SAnTAfBgNVHSMEGDAWgBRF2aWBbj2ITY1x0kbB
-bkUe88SAnTANBgkqhkiG9w0BAQsFAAOCAgEAlDpzBp9SSzBc1P6xXCX5145v9Ydkn+0UjrgEjihL
-j6p7jjm02Vj2e6E1CqGdivdj5eu9OYLU43otb98TPLr+flaYC/NUn81ETm484T4VvwYmneTwkLbU
-wp4wLh/vx3rEUMfqe9pQy3omywC0Wqu1kx+AiYQElY2NfwmTv9SoqORjbdlk5LgpWgi/UOGED1V7
-XwgiG/W9mR4U9s70WBCCswo9GcG/W6uqmdjyMb3lOGbcWAXH7WMaLgqXfIeTK7KK4/HsGOV1timH
-59yLGn602MnTihdsfSlEvoqq9X46Lmgxk7lq2prg2+kupYTNHAq4Sgj5nPFhJpiTt3tm7JFe3VE/
-23MPrQRYCd0EApUKPtN236YQHoA96M2kZNEzx5LH4k5E4wnJTsJdhw4Snr8PyQUQ3nqjsTzyP6Wq
-J3mtMX0f/fwZacXduT98zca0wjAefm6S139hdlqP65VNvBFuIXxZN5nQBrz5Bm0yFqXZaajh3DyA
-HmBR3NdUIR7KYndP+tiPsys6DXhyyWhBWkdKwqPrGtcKqzwyVcgKEZzfdNbwQBUdyLmPtTbFr/gi
-uMod89a2GQ+fYWVq6nTIfI/DT11lgh/ZDYnadXL77/FHZxOzyNEZiCcmmpl5fx7kLD977vHeTYuW
-l8PVP3wbI+2ksx0WckNLIOFZfsLorSa/ovc=
------END CERTIFICATE-----
-
 CA Disig Root R1
 ================
 -----BEGIN CERTIFICATE-----
@@ -3756,7 +3117,7 @@ ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su
 -----END CERTIFICATE-----
 
 TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5
-=========================================================
+====================================================
 -----BEGIN CERTIFICATE-----
 MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN
 BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp
@@ -3779,30 +3140,6 @@ lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8
 B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU=
 -----END CERTIFICATE-----
 
-TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6
-=========================================================
------BEGIN CERTIFICATE-----
-MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G
-A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls
-acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF
-bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5
-MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL
-BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf
-aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm
-aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
-AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a
-2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED
-wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb
-HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV
-+DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT
-9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
-9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R
-fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy
-o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW
-hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1
-O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw==
------END CERTIFICATE-----
-
 Certinomis - Root CA
 ====================
 -----BEGIN CERTIFICATE-----
@@ -3892,28 +3229,418 @@ Daupn75OcsqF1NnstTJFGG+rrQIwfcf3aWMvoeGY7xMQ0Xk/0f7qO3/eVvSQsRUR2LIiFdAvwyYu
 a/GRspBl9JrmkO5K
 -----END CERTIFICATE-----
 
-================================================================
-C: US
-O: GTE Corporation
-OU: GTE CyberTrust Solutions, Inc.
-CN: GTE CyberTrust Global Root
---
-Not Before: 1998-08-13
-Not After:  2018-08-13
-Signature: md5WithRSAEncryption
-Key: RSA:1024
------BEGIN CERTIFICATE-----
-MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
-VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
-bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
-b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
-UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
-cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
-b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
-iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
-r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
-04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
-GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
-3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
-lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
+SZAFIR ROOT CA2
+===============
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV
+BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ
+BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD
+VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q
+qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK
+DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE
+2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ
+ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi
+ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC
+AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5
+O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67
+oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul
+4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6
++/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw==
+-----END CERTIFICATE-----
+
+Certum Trusted Network CA 2
+===========================
+-----BEGIN CERTIFICATE-----
+MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE
+BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1
+bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y
+ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ
+TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB
+IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9
+7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o
+CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b
+Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p
+uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130
+GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ
+9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB
+Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye
+hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM
+BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI
+hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW
+Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA
+L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo
+clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM
+pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb
+w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo
+J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm
+ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX
+is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7
+zAYspsbiDrW5viSP
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions RootCA 2015
+=======================================================
+-----BEGIN CERTIFICATE-----
+MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT
+BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0
+aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl
+YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx
+MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg
+QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV
+BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw
+MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv
+bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh
+iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+
+6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd
+FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr
+i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F
+GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2
+fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu
+iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc
+Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD
+AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI
+hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+
+D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM
+d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y
+d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn
+82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb
+davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F
+Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt
+J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa
+JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q
+p/UsQu0yrbYhnr68
+-----END CERTIFICATE-----
+
+Hellenic Academic and Research Institutions ECC RootCA 2015
+===========================================================
+-----BEGIN CERTIFICATE-----
+MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0
+aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u
+cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj
+aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw
+MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj
+IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD
+VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290
+Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP
+dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK
+Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O
+BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA
+GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn
+dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR
+-----END CERTIFICATE-----
+
+Certplus Root CA G1
+===================
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgISESBVg+QtPlRWhS2DN7cs3EYRMA0GCSqGSIb3DQEBDQUAMD4xCzAJBgNV
+BAYTAkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTAe
+Fw0xNDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhD
+ZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMTCCAiIwDQYJKoZIhvcNAQEBBQAD
+ggIPADCCAgoCggIBANpQh7bauKk+nWT6VjOaVj0W5QOVsjQcmm1iBdTYj+eJZJ+622SLZOZ5KmHN
+r49aiZFluVj8tANfkT8tEBXgfs+8/H9DZ6itXjYj2JizTfNDnjl8KvzsiNWI7nC9hRYt6kuJPKNx
+Qv4c/dMcLRC4hlTqQ7jbxofaqK6AJc96Jh2qkbBIb6613p7Y1/oA/caP0FG7Yn2ksYyy/yARujVj
+BYZHYEMzkPZHogNPlk2dT8Hq6pyi/jQu3rfKG3akt62f6ajUeD94/vI4CTYd0hYCyOwqaK/1jpTv
+LRN6HkJKHRUxrgwEV/xhc/MxVoYxgKDEEW4wduOU8F8ExKyHcomYxZ3MVwia9Az8fXoFOvpHgDm2
+z4QTd28n6v+WZxcIbekN1iNQMLAVdBM+5S//Ds3EC0pd8NgAM0lm66EYfFkuPSi5YXHLtaW6uOrc
+4nBvCGrch2c0798wct3zyT8j/zXhviEpIDCB5BmlIOklynMxdCm+4kLV87ImZsdo/Rmz5yCTmehd
+4F6H50boJZwKKSTUzViGUkAksnsPmBIgJPaQbEfIDbsYIC7Z/fyL8inqh3SV4EJQeIQEQWGw9CEj
+jy3LKCHyamz0GqbFFLQ3ZU+V/YDI+HLlJWvEYLF7bY5KinPOWftwenMGE9nTdDckQQoRb5fc5+R+
+ob0V8rqHDz1oihYHAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0G
+A1UdDgQWBBSowcCbkahDFXxdBie0KlHYlwuBsTAfBgNVHSMEGDAWgBSowcCbkahDFXxdBie0KlHY
+lwuBsTANBgkqhkiG9w0BAQ0FAAOCAgEAnFZvAX7RvUz1isbwJh/k4DgYzDLDKTudQSk0YcbX8ACh
+66Ryj5QXvBMsdbRX7gp8CXrc1cqh0DQT+Hern+X+2B50ioUHj3/MeXrKls3N/U/7/SMNkPX0XtPG
+YX2eEeAC7gkE2Qfdpoq3DIMku4NQkv5gdRE+2J2winq14J2by5BSS7CTKtQ+FjPlnsZlFT5kOwQ/
+2wyPX1wdaR+v8+khjPPvl/aatxm2hHSco1S1cE5j2FddUyGbQJJD+tZ3VTNPZNX70Cxqjm0lpu+F
+6ALEUz65noe8zDUa3qHpimOHZR4RKttjd5cUvpoUmRGywO6wT/gUITJDT5+rosuoD6o7BlXGEilX
+CNQ314cnrUlZp5GrRHpejXDbl85IULFzk/bwg2D5zfHhMf1bfHEhYxQUqq/F3pN+aLHsIqKqkHWe
+tUNy6mSjhEv9DKgma3GX7lZjZuhCVPnHHd/Qj1vfyDBviP4NxDMcU6ij/UgQ8uQKTuEVV/xuZDDC
+VRHc6qnNSlSsKWNEz0pAoNZoWRsz+e86i9sgktxChL8Bq4fA1SCC28a5g4VCXA9DO2pJNdWY9BW/
++mGBDAkgGNLQFwzLSABQ6XaCjGTXOqAHVcweMcDvOrRl++O/QmueD6i9a5jc2NvLi6Td11n0bt3+
+qsOR0C5CB8AMTVPNJLFMWx5R9N/pkvo=
+-----END CERTIFICATE-----
+
+Certplus Root CA G2
+===================
+-----BEGIN CERTIFICATE-----
+MIICHDCCAaKgAwIBAgISESDZkc6uo+jF5//pAq/Pc7xVMAoGCCqGSM49BAMDMD4xCzAJBgNVBAYT
+AkZSMREwDwYDVQQKDAhDZXJ0cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjAeFw0x
+NDA1MjYwMDAwMDBaFw0zODAxMTUwMDAwMDBaMD4xCzAJBgNVBAYTAkZSMREwDwYDVQQKDAhDZXJ0
+cGx1czEcMBoGA1UEAwwTQ2VydHBsdXMgUm9vdCBDQSBHMjB2MBAGByqGSM49AgEGBSuBBAAiA2IA
+BM0PW1aC3/BFGtat93nwHcmsltaeTpwftEIRyoa/bfuFo8XlGVzX7qY/aWfYeOKmycTbLXku54uN
+Am8xIk0G42ByRZ0OQneezs/lf4WbGOT8zC5y0xaTTsqZY1yhBSpsBqNjMGEwDgYDVR0PAQH/BAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMB8GA1Ud
+IwQYMBaAFNqDYwJ5jtpMxjwjFNiPwyCrKGBZMAoGCCqGSM49BAMDA2gAMGUCMHD+sAvZ94OX7PNV
+HdTcswYO/jOYnYs5kGuUIe22113WTNchp+e/IQ8rzfcq3IUHnQIxAIYUFuXcsGXCwI4Un78kFmjl
+vPl5adytRSv3tjFzzAalU5ORGpOucGpnutee5WEaXw==
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G1
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESCzkFU5fX82bWTCp59rY45nMA0GCSqGSIb3DQEBCwUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcx
+MB4XDTE0MDUyNjA4NDU1MFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzEwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQD4eUbalsUwXopxAy1wpLuwxQjczeY1wICkES3d5oeuXT2R0odsN7fa
+Yp6bwiTXj/HbpqbfRm9RpnHLPhsxZ2L3EVs0J9V5ToybWL0iEA1cJwzdMOWo010hOHQX/uMftk87
+ay3bfWAfjH1MBcLrARYVmBSO0ZB3Ij/swjm4eTrwSSTilZHcYTSSjFR077F9jAHiOH3BX2pfJLKO
+YheteSCtqx234LSWSE9mQxAGFiQD4eCcjsZGT44ameGPuY4zbGneWK2gDqdkVBFpRGZPTBKnjix9
+xNRbxQA0MMHZmf4yzgeEtE7NCv82TWLxp2NX5Ntqp66/K7nJ5rInieV+mhxNaMbBGN4zK1FGSxyO
+9z0M+Yo0FMT7MzUj8czxKselu7Cizv5Ta01BG2Yospb6p64KTrk5M0ScdMGTHPjgniQlQ/GbI4Kq
+3ywgsNw2TgOzfALU5nsaqocTvz6hdLubDuHAk5/XpGbKuxs74zD0M1mKB3IDVedzagMxbm+WG+Oi
+n6+Sx+31QrclTDsTBM8clq8cIqPQqwWyTBIjUtz9GVsnnB47ev1CI9sjgBPwvFEVVJSmdz7QdFG9
+URQIOTfLHzSpMJ1ShC5VkLG631UAC9hWLbFJSXKAqWLXwPYYEQRVzXR7z2FwefR7LFxckvzluFqr
+TJOVoSfupb7PcSNCupt2LQIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUl0YhVyE12jZVx/PxN3DlCPaTKbYwHwYDVR0jBBgwFoAUl0YhVyE12jZVx/Px
+N3DlCPaTKbYwDQYJKoZIhvcNAQELBQADggIBAB3dAmB84DWn5ph76kTOZ0BP8pNuZtQ5iSas000E
+PLuHIT839HEl2ku6q5aCgZG27dmxpGWX4m9kWaSW7mDKHyP7Rbr/jyTwyqkxf3kfgLMtMrpkZ2Cv
+uVnN35pJ06iCsfmYlIrM4LvgBBuZYLFGZdwIorJGnkSI6pN+VxbSFXJfLkur1J1juONI5f6ELlgK
+n0Md/rcYkoZDSw6cMoYsYPXpSOqV7XAp8dUv/TW0V8/bhUiZucJvbI/NeJWsZCj9VrDDb8O+WVLh
+X4SPgPL0DTatdrOjteFkdjpY3H1PXlZs5VVZV6Xf8YpmMIzUUmI4d7S+KNfKNsSbBfD4Fdvb8e80
+nR14SohWZ25g/4/Ii+GOvUKpMwpZQhISKvqxnUOOBZuZ2mKtVzazHbYNeS2WuOvyDEsMpZTGMKcm
+GS3tTAZQMPH9WD25SxdfGbRqhFS0OE85og2WaMMolP3tLR9Ka0OWLpABEPs4poEL0L9109S5zvE/
+bw4cHjdx5RiHdRk/ULlepEU0rbDK5uUTdg8xFKmOLZTW1YVNcxVPS/KyPu1svf0OnWZzsD2097+o
+4BGkxK51CUpjAEggpsadCwmKtODmzj7HPiY46SvepghJAwSQiumPv+i2tCqjI40cHLI5kqiPAlxA
+OXXUc0ECd97N4EOH1uS6SsNsEn/+KuYj1oxx
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G2
+====================
+-----BEGIN CERTIFICATE-----
+MIIFbzCCA1egAwIBAgISESChaRu/vbm9UpaPI+hIvyYRMA0GCSqGSIb3DQEBDQUAMEAxCzAJBgNV
+BAYTAkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEcy
+MB4XDTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoM
+CU9wZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzIwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQDMtlelM5QQgTJT32F+D3Y5z1zCU3UdSXqWON2ic2rxb95eolq5cSG+
+Ntmh/LzubKh8NBpxGuga2F8ORAbtp+Dz0mEL4DKiltE48MLaARf85KxP6O6JHnSrT78eCbY2albz
+4e6WiWYkBuTNQjpK3eCasMSCRbP+yatcfD7J6xcvDH1urqWPyKwlCm/61UWY0jUJ9gNDlP7ZvyCV
+eYCYitmJNbtRG6Q3ffyZO6v/v6wNj0OxmXsWEH4db0fEFY8ElggGQgT4hNYdvJGmQr5J1WqIP7wt
+UdGejeBSzFfdNTVY27SPJIjki9/ca1TSgSuyzpJLHB9G+h3Ykst2Z7UJmQnlrBcUVXDGPKBWCgOz
+3GIZ38i1MH/1PCZ1Eb3XG7OHngevZXHloM8apwkQHZOJZlvoPGIytbU6bumFAYueQ4xncyhZW+vj
+3CzMpSZyYhK05pyDRPZRpOLAeiRXyg6lPzq1O4vldu5w5pLeFlwoW5cZJ5L+epJUzpM5ChaHvGOz
+9bGTXOBut9Dq+WIyiET7vycotjCVXRIouZW+j1MY5aIYFuJWpLIsEPUdN6b4t/bQWVyJ98LVtZR0
+0dX+G7bw5tYee9I8y6jj9RjzIR9u701oBnstXW5DiabA+aC/gh7PU3+06yzbXfZqfUAkBXKJOAGT
+y3HCOV0GEfZvePg3DTmEJwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
+/zAdBgNVHQ4EFgQUajn6QiL35okATV59M4PLuG53hq8wHwYDVR0jBBgwFoAUajn6QiL35okATV59
+M4PLuG53hq8wDQYJKoZIhvcNAQENBQADggIBAJjLq0A85TMCl38th6aP1F5Kr7ge57tx+4BkJamz
+Gj5oXScmp7oq4fBXgwpkTx4idBvpkF/wrM//T2h6OKQQbA2xx6R3gBi2oihEdqc0nXGEL8pZ0keI
+mUEiyTCYYW49qKgFbdEfwFFEVn8nNQLdXpgKQuswv42hm1GqO+qTRmTFAHneIWv2V6CG1wZy7HBG
+S4tz3aAhdT7cHcCP009zHIXZ/n9iyJVvttN7jLpTwm+bREx50B1ws9efAvSyB7DH5fitIw6mVskp
+EndI2S9G/Tvw/HRwkqWOOAgfZDC2t0v7NqwQjqBSM2OdAzVWxWm9xiNaJ5T2pBL4LTM8oValX9YZ
+6e18CL13zSdkzJTaTkZQh+D5wVOAHrut+0dSixv9ovneDiK3PTNZbNTe9ZUGMg1RGUFcPk8G97kr
+gCf2o6p6fAbhQ8MTOWIaNr3gKC6UAuQpLmBVrkA9sHSSXvAgZJY/X0VdiLWK2gKgW0VU3jg9CcCo
+SmVGFvyqv1ROTVu+OEO3KMqLM6oaJbolXCkvW0pujOotnCr2BXbgd5eAiN1nE28daCSLT7d0geX0
+YJ96Vdc+N9oWaz53rK4YcJUIeSkDiv7BO7M/Gg+kO14fWKGVyasvc0rQLW6aWQ9VGHgtPFGml4vm
+u7JwqkwR3v98KzfUetF3NI/n+UL3PIEMS1IK
+-----END CERTIFICATE-----
+
+OpenTrust Root CA G3
+====================
+-----BEGIN CERTIFICATE-----
+MIICITCCAaagAwIBAgISESDm+Ez8JLC+BUCs2oMbNGA/MAoGCCqGSM49BAMDMEAxCzAJBgNVBAYT
+AkZSMRIwEAYDVQQKDAlPcGVuVHJ1c3QxHTAbBgNVBAMMFE9wZW5UcnVzdCBSb290IENBIEczMB4X
+DTE0MDUyNjAwMDAwMFoXDTM4MDExNTAwMDAwMFowQDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCU9w
+ZW5UcnVzdDEdMBsGA1UEAwwUT3BlblRydXN0IFJvb3QgQ0EgRzMwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAARK7liuTcpm3gY6oxH84Bjwbhy6LTAMidnW7ptzg6kjFYwvWYpa3RTqnVkrQ7cG7DK2uu5B
+ta1doYXM6h0UZqNnfkbilPPntlahFVmhTzeXuSIevRHr9LIfXsMUmuXZl5mjYzBhMA4GA1UdDwEB
+/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAf
+BgNVHSMEGDAWgBRHd8MUi2I5DMlv4VBN0BBY3JWIbTAKBggqhkjOPQQDAwNpADBmAjEAj6jcnboM
+BBf6Fek9LykBl7+BFjNAk2z8+e2AcG+qj9uEwov1NcoG3GRvaBbhj5G5AjEA2Euly8LQCGzpGPta
+3U1fJAuwACEl74+nBCZx4nxp5V2a+EEfOzmTk51V6s2N8fvB
+-----END CERTIFICATE-----
+
+ISRG Root X1
+============
+-----BEGIN CERTIFICATE-----
+MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE
+BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD
+EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG
+EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT
+DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r
+Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1
+3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K
+b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN
+Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ
+4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf
+1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu
+hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH
+usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r
+OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G
+A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY
+9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
+ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV
+0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt
+hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw
+TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx
+e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA
+JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD
+YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n
+JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ
+m+kXQ99b21/+jh5Xos1AnX5iItreGCc=
+-----END CERTIFICATE-----
+
+AC RAIZ FNMT-RCM
+================
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT
+AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw
+MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD
+TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf
+qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr
+btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL
+j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou
+08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw
+WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT
+tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ
+47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC
+ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa
+i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
+FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o
+dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s
+D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ
+j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT
+Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW
++YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7
+Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d
+8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm
+5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG
+rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
+
+Amazon Root CA 1
+================
+-----BEGIN CERTIFICATE-----
+MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1
+MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH
+FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ
+gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t
+dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce
+VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3
+DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM
+CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy
+8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa
+2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2
+xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5
+-----END CERTIFICATE-----
+
+Amazon Root CA 2
+================
+-----BEGIN CERTIFICATE-----
+MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD
+VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1
+MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv
+bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4
+kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp
+N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9
+AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd
+fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx
+kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS
+btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0
+Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN
+c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+
+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw
+DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA
+A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY
++gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE
+YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW
+xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ
+gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW
+aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV
+Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3
+KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi
+JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw=
+-----END CERTIFICATE-----
+
+Amazon Root CA 3
+================
+-----BEGIN CERTIFICATE-----
+MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB
+f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr
+Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43
+rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc
+eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw==
+-----END CERTIFICATE-----
+
+Amazon Root CA 4
+================
+-----BEGIN CERTIFICATE-----
+MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG
+EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy
+NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ
+MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN
+/sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri
+83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV
+HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA
+MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1
+AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA==
+-----END CERTIFICATE-----
+
+LuxTrust Global Root 2
+======================
+-----BEGIN CERTIFICATE-----
+MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQELBQAwRjELMAkG
+A1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNVBAMMFkx1eFRydXN0IEdsb2Jh
+bCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUwMzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEW
+MBQGA1UECgwNTHV4VHJ1c3QgUy5BLjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCC
+AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wm
+Kb3FibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTemhfY7RBi2
+xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1EMShduxq3sVs35a0VkBC
+wGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsnXpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm
+1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkm
+FRseTJIpgp7VkoGSQXAZ96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niF
+wpN6cj5mj5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4gDEa/
+a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+8kPREd8vZS9kzl8U
+ubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2jX5t/Lax5Gw5CMZdjpPuKadUiDTSQ
+MC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmHhFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB
+/zBCBgNVHSAEOzA5MDcGByuBKwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5
+Lmx1eHRydXN0Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT
++Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQELBQADggIBAGoZ
+FO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9BzZAcg4atmpZ1gDlaCDdLnIN
+H2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTOjFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW
+7MM3LGVYvlcAGvI1+ut7MV3CwRI9loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIu
+ZY+kt9J/Z93I055cqqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWA
+VWe+2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/JEAdemrR
+TxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKrezrnK+T+Tb/mjuuqlPpmt
+/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQfLSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc
+7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31I
+iyBMz2TWuJdGsE7RKlY6oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr
+-----END CERTIFICATE-----
+
+TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1
+=============================================
+-----BEGIN CERTIFICATE-----
+MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT
+D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr
+IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g
+TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp
+ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD
+VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt
+c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth
+bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11
+IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8
+6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc
+wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0
+3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9
+WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU
+ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh
+AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc
+lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R
+e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j
+q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM=
 -----END CERTIFICATE-----

BIN
plugins/stripe/data/test.png


+ 51 - 3
plugins/stripe/init.php

@@ -5,6 +5,9 @@ require(dirname(__FILE__) . '/lib/Stripe.php');
 
 // Utilities
 require(dirname(__FILE__) . '/lib/Util/AutoPagingIterator.php');
+require(dirname(__FILE__) . '/lib/Util/LoggerInterface.php');
+require(dirname(__FILE__) . '/lib/Util/DefaultLogger.php');
+require(dirname(__FILE__) . '/lib/Util/RandomGenerator.php');
 require(dirname(__FILE__) . '/lib/Util/RequestOptions.php');
 require(dirname(__FILE__) . '/lib/Util/Set.php');
 require(dirname(__FILE__) . '/lib/Util/Util.php');
@@ -19,19 +22,36 @@ require(dirname(__FILE__) . '/lib/Error/Api.php');
 require(dirname(__FILE__) . '/lib/Error/ApiConnection.php');
 require(dirname(__FILE__) . '/lib/Error/Authentication.php');
 require(dirname(__FILE__) . '/lib/Error/Card.php');
+require(dirname(__FILE__) . '/lib/Error/Idempotency.php');
 require(dirname(__FILE__) . '/lib/Error/InvalidRequest.php');
 require(dirname(__FILE__) . '/lib/Error/Permission.php');
 require(dirname(__FILE__) . '/lib/Error/RateLimit.php');
+require(dirname(__FILE__) . '/lib/Error/SignatureVerification.php');
+
+// OAuth errors
+require(dirname(__FILE__) . '/lib/Error/OAuth/OAuthBase.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidClient.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidGrant.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidRequest.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/InvalidScope.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedGrantType.php');
+require(dirname(__FILE__) . '/lib/Error/OAuth/UnsupportedResponseType.php');
+
+// API operations
+require(dirname(__FILE__) . '/lib/ApiOperations/All.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Create.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Delete.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/NestedResource.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Request.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Retrieve.php');
+require(dirname(__FILE__) . '/lib/ApiOperations/Update.php');
 
 // Plumbing
 require(dirname(__FILE__) . '/lib/ApiResponse.php');
-require(dirname(__FILE__) . '/lib/JsonSerializable.php');
 require(dirname(__FILE__) . '/lib/StripeObject.php');
 require(dirname(__FILE__) . '/lib/ApiRequestor.php');
 require(dirname(__FILE__) . '/lib/ApiResource.php');
 require(dirname(__FILE__) . '/lib/SingletonApiResource.php');
-require(dirname(__FILE__) . '/lib/AttachedObject.php');
-require(dirname(__FILE__) . '/lib/ExternalAccount.php');
 
 // Stripe API Resources
 require(dirname(__FILE__) . '/lib/Account.php');
@@ -50,22 +70,50 @@ require(dirname(__FILE__) . '/lib/Collection.php');
 require(dirname(__FILE__) . '/lib/CountrySpec.php');
 require(dirname(__FILE__) . '/lib/Coupon.php');
 require(dirname(__FILE__) . '/lib/Customer.php');
+require(dirname(__FILE__) . '/lib/Discount.php');
 require(dirname(__FILE__) . '/lib/Dispute.php');
+require(dirname(__FILE__) . '/lib/EphemeralKey.php');
 require(dirname(__FILE__) . '/lib/Event.php');
+require(dirname(__FILE__) . '/lib/ExchangeRate.php');
+require(dirname(__FILE__) . '/lib/FileLink.php');
 require(dirname(__FILE__) . '/lib/FileUpload.php');
 require(dirname(__FILE__) . '/lib/Invoice.php');
 require(dirname(__FILE__) . '/lib/InvoiceItem.php');
+require(dirname(__FILE__) . '/lib/InvoiceLineItem.php');
+require(dirname(__FILE__) . '/lib/IssuerFraudRecord.php');
+require(dirname(__FILE__) . '/lib/Issuing/Authorization.php');
+require(dirname(__FILE__) . '/lib/Issuing/Card.php');
+require(dirname(__FILE__) . '/lib/Issuing/CardDetails.php');
+require(dirname(__FILE__) . '/lib/Issuing/Cardholder.php');
+require(dirname(__FILE__) . '/lib/Issuing/Dispute.php');
+require(dirname(__FILE__) . '/lib/Issuing/Transaction.php');
+require(dirname(__FILE__) . '/lib/LoginLink.php');
 require(dirname(__FILE__) . '/lib/Order.php');
+require(dirname(__FILE__) . '/lib/OrderItem.php');
 require(dirname(__FILE__) . '/lib/OrderReturn.php');
+require(dirname(__FILE__) . '/lib/PaymentIntent.php');
+require(dirname(__FILE__) . '/lib/Payout.php');
 require(dirname(__FILE__) . '/lib/Plan.php');
 require(dirname(__FILE__) . '/lib/Product.php');
 require(dirname(__FILE__) . '/lib/Recipient.php');
+require(dirname(__FILE__) . '/lib/RecipientTransfer.php');
 require(dirname(__FILE__) . '/lib/Refund.php');
 require(dirname(__FILE__) . '/lib/SKU.php');
+require(dirname(__FILE__) . '/lib/Sigma/ScheduledQueryRun.php');
 require(dirname(__FILE__) . '/lib/Source.php');
+require(dirname(__FILE__) . '/lib/SourceTransaction.php');
 require(dirname(__FILE__) . '/lib/Subscription.php');
 require(dirname(__FILE__) . '/lib/SubscriptionItem.php');
 require(dirname(__FILE__) . '/lib/ThreeDSecure.php');
 require(dirname(__FILE__) . '/lib/Token.php');
+require(dirname(__FILE__) . '/lib/Topup.php');
 require(dirname(__FILE__) . '/lib/Transfer.php');
 require(dirname(__FILE__) . '/lib/TransferReversal.php');
+require(dirname(__FILE__) . '/lib/UsageRecord.php');
+
+// OAuth
+require(dirname(__FILE__) . '/lib/OAuth.php');
+
+// Webhooks
+require(dirname(__FILE__) . '/lib/Webhook.php');
+require(dirname(__FILE__) . '/lib/WebhookSignature.php');

+ 144 - 35
plugins/stripe/lib/Account.php

@@ -7,35 +7,67 @@ namespace Stripe;
  *
  * @property string $id
  * @property string $object
- * @property mixed $business_logo
+ * @property string $business_logo
  * @property string $business_name
- * @property mixed $business_url
+ * @property string $business_primary_color
+ * @property string $business_url
  * @property bool $charges_enabled
  * @property string $country
+ * @property int $created
  * @property bool $debit_negative_balances
  * @property mixed $decline_charge_on
  * @property string $default_currency
  * @property bool $details_submitted
  * @property string $display_name
  * @property string $email
- * @property mixed $external_accounts
+ * @property Collection $external_accounts
  * @property mixed $legal_entity
- * @property bool $managed
- * @property mixed $product_description
- * @property mixed $statement_descriptor
- * @property mixed $support_email
- * @property mixed $support_phone
+ * @property StripeObject $metadata
+ * @property mixed $payout_schedule
+ * @property string $payout_statement_descriptor
+ * @property bool $payouts_enabled
+ * @property string $product_description
+ * @property string $statement_descriptor
+ * @property mixed $support_address
+ * @property string $support_email
+ * @property string $support_phone
+ * @property string $support_url
  * @property string $timezone
  * @property mixed $tos_acceptance
- * @property mixed $transfer_schedule
- * @property bool $transfers_enabled
+ * @property string $type
  * @property mixed $verification
- * @property mixed $keys
  *
  * @package Stripe
  */
 class Account extends ApiResource
 {
+
+    const OBJECT_NAME = "account";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve {
+        retrieve as protected _retrieve;
+    }
+    use ApiOperations\Update;
+
+    public static function getSavedNestedResources()
+    {
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'external_account',
+                'bank_account',
+            ]);
+        }
+        return $savedNestedResources;
+    }
+
+    const PATH_EXTERNAL_ACCOUNTS = '/external_accounts';
+    const PATH_LOGIN_LINKS = '/login_links';
+
     public function instanceUrl()
     {
         if ($this['id'] === null) {
@@ -46,7 +78,8 @@ class Account extends ApiResource
     }
 
     /**
-     * @param string|null $id
+     * @param array|string|null $id The ID of the account to retrieve, or an
+     *     options array containing an `id` key.
      * @param array|string|null $opts
      *
      * @return Account
@@ -64,68 +97,144 @@ class Account extends ApiResource
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Account
+     * @return Account The rejected account.
      */
-    public static function create($params = null, $opts = null)
+    public function reject($params = null, $opts = null)
     {
-        return self::_create($params, $opts);
+        $url = $this->instanceUrl() . '/reject';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
     }
 
     /**
-     * @param string $id The ID of the account to update.
+     * @param array|null $clientId
+     * @param array|string|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public function deauthorize($clientId = null, $opts = null)
+    {
+        $params = [
+            'client_id' => $clientId,
+            'stripe_user_id' => $this->id,
+        ];
+        return OAuth::deauthorize($params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account on which to create the external account.
      * @param array|null $params
-     * @param array|string|null $options
+     * @param array|string|null $opts
      *
-     * @return Account The updated account.
+     * @return BankAccount|Card
      */
-    public static function update($id, $params = null, $options = null)
+    public static function createExternalAccount($id, $params = null, $opts = null)
     {
-        return self::_update($id, $params, $options);
+        return self::_createNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to retrieve.
+     * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Account
+     * @return BankAccount|Card
      */
-    public function save($opts = null)
+    public static function retrieveExternalAccount($id, $externalAccountId, $params = null, $opts = null)
     {
-        return $this->_save($opts);
+        return self::_retrieveNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to update.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Account The deleted account.
+     * @return BankAccount|Card
      */
-    public function delete($params = null, $opts = null)
+    public static function updateExternalAccount($id, $externalAccountId, $params = null, $opts = null)
     {
-        return $this->_delete($params, $opts);
+        return self::_updateNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the account to which the external account belongs.
+     * @param array|null $externalAccountId The ID of the external account to delete.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Account The rejected account.
+     * @return BankAccount|Card
      */
-    public function reject($params = null, $opts = null)
+    public static function deleteExternalAccount($id, $externalAccountId, $params = null, $opts = null)
     {
-        $url = $this->instanceUrl() . '/reject';
-        list($response, $opts) = $this->_request('post', $url, $params, $opts);
-        $this->refreshFrom($response, $opts);
-        return $this;
+        return self::_deleteNestedResource($id, static::PATH_EXTERNAL_ACCOUNTS, $externalAccountId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the account on which to retrieve the external accounts.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return BankAccount|Card
+     */
+    public static function allExternalAccounts($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_EXTERNAL_ACCOUNTS, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the account on which to create the login link.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Collection of Accounts
+     * @return LoginLink
      */
-    public static function all($params = null, $opts = null)
+    public static function createLoginLink($id, $params = null, $opts = null)
     {
-        return self::_all($params, $opts);
+        return self::_createNestedResource($id, static::PATH_LOGIN_LINKS, $params, $opts);
+    }
+
+    public function serializeParameters($force = false)
+    {
+        $update = parent::serializeParameters($force);
+        if (isset($this->_values['legal_entity'])) {
+            $entity = $this['legal_entity'];
+            if (isset($entity->_values['additional_owners'])) {
+                $owners = $entity['additional_owners'];
+                $entityUpdate = isset($update['legal_entity']) ? $update['legal_entity'] : [];
+                $entityUpdate['additional_owners'] = $this->serializeAdditionalOwners($entity, $owners);
+                $update['legal_entity'] = $entityUpdate;
+            }
+        }
+        return $update;
+    }
+
+    private function serializeAdditionalOwners($legalEntity, $additionalOwners)
+    {
+        if (isset($legalEntity->_originalValues['additional_owners'])) {
+            $originalValue = $legalEntity->_originalValues['additional_owners'];
+        } else {
+            $originalValue = [];
+        }
+        if (($originalValue) && (count($originalValue) > count($additionalOwners))) {
+            throw new \InvalidArgumentException(
+                "You cannot delete an item from an array, you must instead set a new array"
+            );
+        }
+
+        $updateArr = [];
+        foreach ($additionalOwners as $i => $v) {
+            $update = ($v instanceof StripeObject) ? $v->serializeParameters() : $v;
+
+            if ($update !== []) {
+                if (!$originalValue || ($update != $legalEntity->serializeParamsValue($originalValue[$i], null, false, true))) {
+                    $updateArr[$i] = $update;
+                }
+            }
+        }
+        return $updateArr;
     }
 }

+ 60 - 1
plugins/stripe/lib/AlipayAccount.php

@@ -6,8 +6,67 @@ namespace Stripe;
  * Class AlipayAccount
  *
  * @package Stripe
+ *
+ * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+ * @link https://stripe.com/docs/sources/alipay
  */
-class AlipayAccount extends ExternalAccount
+class AlipayAccount extends ApiResource
 {
 
+    const OBJECT_NAME = "alipay_account";
+
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } else {
+            $msg = "Alipay accounts cannot be accessed without a customer ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     *
+     * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+     * @link https://stripe.com/docs/sources/alipay
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Alipay accounts cannot be accessed without a customer ID. " .
+               "Retrieve an Alipay account using \$customer->sources->retrieve('alipay_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+    /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     *
+     * @deprecated Alipay accounts are deprecated. Please use the sources API instead.
+     * @link https://stripe.com/docs/sources/alipay
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Alipay accounts cannot be accessed without a customer ID. " .
+               "Call save() on \$customer->sources->retrieve('alipay_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
 }

+ 34 - 0
plugins/stripe/lib/ApiOperations/All.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for listable resources. Adds a `all()` static method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait All
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return \Stripe\Collection of ApiResources
+     */
+    public static function all($params = null, $opts = null)
+    {
+        self::_validateParams($params);
+        $url = static::classUrl();
+
+        list($response, $opts) = static::_staticRequest('get', $url, $params, $opts);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        if (!is_a($obj, 'Stripe\\Collection')) {
+            $class = get_class($obj);
+            $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead";
+            throw new \Stripe\Error\Api($message);
+        }
+        $obj->setLastResponse($response);
+        $obj->setRequestParams($params);
+        return $obj;
+    }
+}

+ 28 - 0
plugins/stripe/lib/ApiOperations/Create.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for creatable resources. Adds a `create()` static method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Create
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\ApiResource The created resource.
+     */
+    public static function create($params = null, $options = null)
+    {
+        self::_validateParams($params);
+        $url = static::classUrl();
+
+        list($response, $opts) = static::_staticRequest('post', $url, $params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+}

+ 27 - 0
plugins/stripe/lib/ApiOperations/Delete.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for deletable resources. Adds a `delete()` method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Delete
+{
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return \Stripe\ApiResource The deleted resource.
+     */
+    public function delete($params = null, $opts = null)
+    {
+        self::_validateParams($params);
+
+        $url = $this->instanceUrl();
+        list($response, $opts) = $this->_request('delete', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}

+ 115 - 0
plugins/stripe/lib/ApiOperations/NestedResource.php

@@ -0,0 +1,115 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for resources that have nested resources.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait NestedResource
+{
+    /**
+     * @param string $method
+     * @param string $url
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _nestedResourceOperation($method, $url, $params = null, $options = null)
+    {
+        self::_validateParams($params);
+
+        list($response, $opts) = static::_staticRequest($method, $url, $params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param string|null $nestedId
+     *
+     * @return string
+     */
+    protected static function _nestedResourceUrl($id, $nestedPath, $nestedId = null)
+    {
+        $url = static::resourceUrl($id) . $nestedPath;
+        if ($nestedId !== null) {
+            $url .= "/$nestedId";
+        }
+        return $url;
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _createNestedResource($id, $nestedPath, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath);
+        return self::_nestedResourceOperation('post', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _retrieveNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('get', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _updateNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('post', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _deleteNestedResource($id, $nestedPath, $nestedId, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath, $nestedId);
+        return self::_nestedResourceOperation('delete', $url, $params, $options);
+    }
+
+    /**
+     * @param string $id
+     * @param string $nestedPath
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\StripeObject
+     */
+    protected static function _allNestedResources($id, $nestedPath, $params = null, $options = null)
+    {
+        $url = static::_nestedResourceUrl($id, $nestedPath);
+        return self::_nestedResourceOperation('get', $url, $params, $options);
+    }
+}

+ 60 - 0
plugins/stripe/lib/ApiOperations/Request.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for resources that need to make API requests.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Request
+{
+    /**
+     * @param array|null|mixed $params The list of parameters to validate
+     *
+     * @throws \Stripe\Error\Api if $params exists and is not an array
+     */
+    protected static function _validateParams($params = null)
+    {
+        if ($params && !is_array($params)) {
+            $message = "You must pass an array as the first argument to Stripe API "
+               . "method calls.  (HINT: an example call to create a charge "
+               . "would be: \"Stripe\\Charge::create(['amount' => 100, "
+               . "'currency' => 'usd', 'source' => 'tok_1234'])\")";
+            throw new \Stripe\Error\Api($message);
+        }
+    }
+
+    /**
+     * @param string $method HTTP method ('get', 'post', etc.)
+     * @param string $url URL for the request
+     * @param array $params list of parameters for the request
+     * @param array|string|null $options
+     *
+     * @return array tuple containing (the JSON response, $options)
+     */
+    protected function _request($method, $url, $params = [], $options = null)
+    {
+        $opts = $this->_opts->merge($options);
+        list($resp, $options) = static::_staticRequest($method, $url, $params, $opts);
+        $this->setLastResponse($resp);
+        return [$resp->json, $options];
+    }
+
+    /**
+     * @param string $method HTTP method ('get', 'post', etc.)
+     * @param string $url URL for the request
+     * @param array $params list of parameters for the request
+     * @param array|string|null $options
+     *
+     * @return array tuple containing (the JSON response, $options)
+     */
+    protected static function _staticRequest($method, $url, $params, $options)
+    {
+        $opts = \Stripe\Util\RequestOptions::parse($options);
+        $requestor = new \Stripe\ApiRequestor($opts->apiKey, static::baseUrl());
+        list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers);
+        $opts->discardNonPersistentHeaders();
+        return [$response, $opts];
+    }
+}

+ 27 - 0
plugins/stripe/lib/ApiOperations/Retrieve.php

@@ -0,0 +1,27 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for retrievable resources. Adds a `retrieve()` static method to the
+ * class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Retrieve
+{
+    /**
+     * @param array|string $id The ID of the API resource to retrieve,
+     *     or an options array containing an `id` key.
+     * @param array|string|null $opts
+     *
+     * @return \Stripe\StripeObject
+     */
+    public static function retrieve($id, $opts = null)
+    {
+        $opts = \Stripe\Util\RequestOptions::parse($opts);
+        $instance = new static($id, $opts);
+        $instance->refresh();
+        return $instance;
+    }
+}

+ 46 - 0
plugins/stripe/lib/ApiOperations/Update.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace Stripe\ApiOperations;
+
+/**
+ * Trait for updatable resources. Adds an `update()` static method and a
+ * `save()` method to the class.
+ *
+ * This trait should only be applied to classes that derive from StripeObject.
+ */
+trait Update
+{
+    /**
+     * @param string $id The ID of the resource to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return \Stripe\ApiResource The updated resource.
+     */
+    public static function update($id, $params = null, $opts = null)
+    {
+        self::_validateParams($params);
+        $url = static::resourceUrl($id);
+
+        list($response, $opts) = static::_staticRequest('post', $url, $params, $opts);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+
+    /**
+     * @param array|string|null $opts
+     *
+     * @return \Stripe\ApiResource The saved resource.
+     */
+    public function save($opts = null)
+    {
+        $params = $this->serializeParameters();
+        if (count($params) > 0) {
+            $url = $this->instanceUrl();
+            list($response, $opts) = $this->_request('post', $url, $params, $opts);
+            $this->refreshFrom($response, $opts);
+        }
+        return $this;
+    }
+}

+ 212 - 40
plugins/stripe/lib/ApiRequestor.php

@@ -9,12 +9,27 @@ namespace Stripe;
  */
 class ApiRequestor
 {
+    /**
+     * @var string|null
+     */
     private $_apiKey;
 
+    /**
+     * @var string
+     */
     private $_apiBase;
 
+    /**
+     * @var HttpClient\ClientInterface
+     */
     private static $_httpClient;
 
+    /**
+     * ApiRequestor constructor.
+     *
+     * @param string|null $apiKey
+     * @param string|null $apiBase
+     */
     public function __construct($apiKey = null, $apiBase = null)
     {
         $this->_apiKey = $apiKey;
@@ -24,6 +39,13 @@ class ApiRequestor
         $this->_apiBase = $apiBase;
     }
 
+    /**
+     * @static
+     *
+     * @param ApiResource|bool|array|mixed $d
+     *
+     * @return ApiResource|array|string|mixed
+     */
     private static function _encodeObjects($d)
     {
         if ($d instanceof ApiResource) {
@@ -33,7 +55,7 @@ class ApiRequestor
         } elseif ($d === false) {
             return 'false';
         } elseif (is_array($d)) {
-            $res = array();
+            $res = [];
             foreach ($d as $k => $v) {
                 $res[$k] = self::_encodeObjects($v);
             }
@@ -44,27 +66,37 @@ class ApiRequestor
     }
 
     /**
-     * @param string $method
-     * @param string $url
+     * @param string     $method
+     * @param string     $url
      * @param array|null $params
      * @param array|null $headers
      *
      * @return array An array whose first element is an API response and second
      *    element is the API key used to make the request.
+     * @throws Error\Api
+     * @throws Error\Authentication
+     * @throws Error\Card
+     * @throws Error\InvalidRequest
+     * @throws Error\OAuth\InvalidClient
+     * @throws Error\OAuth\InvalidGrant
+     * @throws Error\OAuth\InvalidRequest
+     * @throws Error\OAuth\InvalidScope
+     * @throws Error\OAuth\UnsupportedGrantType
+     * @throws Error\OAuth\UnsupportedResponseType
+     * @throws Error\Permission
+     * @throws Error\RateLimit
+     * @throws Error\Idempotency
+     * @throws Error\ApiConnection
      */
     public function request($method, $url, $params = null, $headers = null)
     {
-        if (!$params) {
-            $params = array();
-        }
-        if (!$headers) {
-            $headers = array();
-        }
+        $params = $params ?: [];
+        $headers = $headers ?: [];
         list($rbody, $rcode, $rheaders, $myApiKey) =
         $this->_requestRaw($method, $url, $params, $headers);
         $json = $this->_interpretResponse($rbody, $rcode, $rheaders);
         $resp = new ApiResponse($rbody, $rcode, $rheaders, $json);
-        return array($resp, $myApiKey);
+        return [$resp, $myApiKey];
     }
 
     /**
@@ -80,11 +112,21 @@ class ApiRequestor
      *    permissions.
      * @throws Error\Card if the error is the error code is 402 (payment
      *    required)
+     * @throws Error\InvalidRequest if the error is caused by the user.
+     * @throws Error\Idempotency if the error is caused by an idempotency key.
+     * @throws Error\OAuth\InvalidClient
+     * @throws Error\OAuth\InvalidGrant
+     * @throws Error\OAuth\InvalidRequest
+     * @throws Error\OAuth\InvalidScope
+     * @throws Error\OAuth\UnsupportedGrantType
+     * @throws Error\OAuth\UnsupportedResponseType
+     * @throws Error\Permission if the error is caused by insufficient
+     *    permissions.
      * @throws Error\RateLimit if the error is caused by too many requests
      *    hitting the API.
      * @throws Error\Api otherwise.
      */
-    public function handleApiError($rbody, $rcode, $rheaders, $resp)
+    public function handleErrorResponse($rbody, $rcode, $rheaders, $resp)
     {
         if (!is_array($resp) || !isset($resp['error'])) {
             $msg = "Invalid response object from API: $rbody "
@@ -92,35 +134,104 @@ class ApiRequestor
             throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders);
         }
 
-        $error = $resp['error'];
-        $msg = isset($error['message']) ? $error['message'] : null;
-        $param = isset($error['param']) ? $error['param'] : null;
-        $code = isset($error['code']) ? $error['code'] : null;
+        $errorData = $resp['error'];
+
+        $error = null;
+        if (is_string($errorData)) {
+            $error = self::_specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorData);
+        }
+        if (!$error) {
+            $error = self::_specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData);
+        }
+
+        throw $error;
+    }
+
+    /**
+     * @static
+     *
+     * @param string $rbody
+     * @param int    $rcode
+     * @param array  $rheaders
+     * @param array  $resp
+     * @param array  $errorData
+     *
+     * @return Error\RateLimit|Error\Idempotency|Error\InvalidRequest|Error\Authentication|Error\Card|Error\Permission|Error\Api
+     */
+    private static function _specificAPIError($rbody, $rcode, $rheaders, $resp, $errorData)
+    {
+        $msg = isset($errorData['message']) ? $errorData['message'] : null;
+        $param = isset($errorData['param']) ? $errorData['param'] : null;
+        $code = isset($errorData['code']) ? $errorData['code'] : null;
+        $type = isset($errorData['type']) ? $errorData['type'] : null;
 
         switch ($rcode) {
             case 400:
                 // 'rate_limit' code is deprecated, but left here for backwards compatibility
                 // for API versions earlier than 2015-09-08
                 if ($code == 'rate_limit') {
-                    throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
+                    return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
+                }
+                if ($type == 'idempotency_error') {
+                    return new Error\Idempotency($msg, $rcode, $rbody, $resp, $rheaders);
                 }
 
                 // intentional fall-through
             case 404:
-                throw new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders);
+                return new Error\InvalidRequest($msg, $param, $rcode, $rbody, $resp, $rheaders);
             case 401:
-                throw new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders);
+                return new Error\Authentication($msg, $rcode, $rbody, $resp, $rheaders);
             case 402:
-                throw new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders);
+                return new Error\Card($msg, $param, $code, $rcode, $rbody, $resp, $rheaders);
             case 403:
-                throw new Error\Permission($msg, $rcode, $rbody, $resp, $rheaders);
+                return new Error\Permission($msg, $rcode, $rbody, $resp, $rheaders);
             case 429:
-                throw new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
+                return new Error\RateLimit($msg, $param, $rcode, $rbody, $resp, $rheaders);
             default:
-                throw new Error\Api($msg, $rcode, $rbody, $resp, $rheaders);
+                return new Error\Api($msg, $rcode, $rbody, $resp, $rheaders);
+        }
+    }
+
+    /**
+     * @static
+     *
+     * @param string|bool $rbody
+     * @param int         $rcode
+     * @param array       $rheaders
+     * @param array       $resp
+     * @param string      $errorCode
+     *
+     * @return null|Error\OAuth\InvalidClient|Error\OAuth\InvalidGrant|Error\OAuth\InvalidRequest|Error\OAuth\InvalidScope|Error\OAuth\UnsupportedGrantType|Error\OAuth\UnsupportedResponseType
+     */
+    private static function _specificOAuthError($rbody, $rcode, $rheaders, $resp, $errorCode)
+    {
+        $description = isset($resp['error_description']) ? $resp['error_description'] : $errorCode;
+
+        switch ($errorCode) {
+            case 'invalid_client':
+                return new Error\OAuth\InvalidClient($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_grant':
+                return new Error\OAuth\InvalidGrant($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_request':
+                return new Error\OAuth\InvalidRequest($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'invalid_scope':
+                return new Error\OAuth\InvalidScope($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'unsupported_grant_type':
+                return new Error\OAuth\UnsupportedGrantType($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
+            case 'unsupported_response_type':
+                return new Error\OAuth\UnsupportedResponseType($errorCode, $description, $rcode, $rbody, $resp, $rheaders);
         }
+
+        return null;
     }
 
+    /**
+     * @static
+     *
+     * @param null|array $appInfo
+     *
+     * @return null|string
+     */
     private static function _formatAppInfo($appInfo)
     {
         if ($appInfo !== null) {
@@ -137,38 +248,56 @@ class ApiRequestor
         }
     }
 
-    private static function _defaultHeaders($apiKey)
+    /**
+     * @static
+     *
+     * @param string $apiKey
+     * @param null   $clientInfo
+     *
+     * @return array
+     */
+    private static function _defaultHeaders($apiKey, $clientInfo = null)
     {
-        $appInfo = Stripe::getAppInfo();
-
         $uaString = 'Stripe/v1 PhpBindings/' . Stripe::VERSION;
 
         $langVersion = phpversion();
         $uname = php_uname();
-        $curlVersion = curl_version();
+
         $appInfo = Stripe::getAppInfo();
-        $ua = array(
+        $ua = [
             'bindings_version' => Stripe::VERSION,
             'lang' => 'php',
             'lang_version' => $langVersion,
             'publisher' => 'stripe',
             'uname' => $uname,
-            'httplib' => 'curl ' . $curlVersion['version'],
-            'ssllib' => $curlVersion['ssl_version'],
-        );
+        ];
+        if ($clientInfo) {
+            $ua = array_merge($clientInfo, $ua);
+        }
         if ($appInfo !== null) {
             $uaString .= ' ' . self::_formatAppInfo($appInfo);
             $ua['application'] = $appInfo;
         }
 
-        $defaultHeaders = array(
+        $defaultHeaders = [
             'X-Stripe-Client-User-Agent' => json_encode($ua),
             'User-Agent' => $uaString,
             'Authorization' => 'Bearer ' . $apiKey,
-        );
+        ];
         return $defaultHeaders;
     }
 
+    /**
+     * @param string $method
+     * @param string $url
+     * @param array  $params
+     * @param array  $headers
+     *
+     * @return array
+     * @throws Error\Api
+     * @throws Error\ApiConnection
+     * @throws Error\Authentication
+     */
     private function _requestRaw($method, $url, $params, $headers)
     {
         $myApiKey = $this->_apiKey;
@@ -184,9 +313,17 @@ class ApiRequestor
             throw new Error\Authentication($msg);
         }
 
+        // Clients can supply arbitrary additional keys to be included in the
+        // X-Stripe-Client-User-Agent header via the optional getUserAgentInfo()
+        // method
+        $clientUAInfo = null;
+        if (method_exists($this->httpClient(), 'getUserAgentInfo')) {
+            $clientUAInfo = $this->httpClient()->getUserAgentInfo();
+        }
+
         $absUrl = $this->_apiBase.$url;
         $params = self::_encodeObjects($params);
-        $defaultHeaders = $this->_defaultHeaders($myApiKey);
+        $defaultHeaders = $this->_defaultHeaders($myApiKey, $clientUAInfo);
         if (Stripe::$apiVersion) {
             $defaultHeaders['Stripe-Version'] = Stripe::$apiVersion;
         }
@@ -213,7 +350,7 @@ class ApiRequestor
         }
 
         $combinedHeaders = array_merge($defaultHeaders, $headers);
-        $rawHeaders = array();
+        $rawHeaders = [];
 
         foreach ($combinedHeaders as $header => $value) {
             $rawHeaders[] = $header . ': ' . $value;
@@ -226,9 +363,16 @@ class ApiRequestor
             $params,
             $hasFile
         );
-        return array($rbody, $rcode, $rheaders, $myApiKey);
+        return [$rbody, $rcode, $rheaders, $myApiKey];
     }
 
+    /**
+     * @param resource $resource
+     * @param bool     $hasCurlFile
+     *
+     * @return \CURLFile|string
+     * @throws Error\Api
+     */
     private function _processResourceParam($resource, $hasCurlFile)
     {
         if (get_resource_type($resource) !== 'stream') {
@@ -252,27 +396,55 @@ class ApiRequestor
         }
     }
 
+    /**
+     * @param string $rbody
+     * @param int    $rcode
+     * @param array  $rheaders
+     *
+     * @return mixed
+     * @throws Error\Api
+     * @throws Error\Authentication
+     * @throws Error\Card
+     * @throws Error\InvalidRequest
+     * @throws Error\OAuth\InvalidClient
+     * @throws Error\OAuth\InvalidGrant
+     * @throws Error\OAuth\InvalidRequest
+     * @throws Error\OAuth\InvalidScope
+     * @throws Error\OAuth\UnsupportedGrantType
+     * @throws Error\OAuth\UnsupportedResponseType
+     * @throws Error\Permission
+     * @throws Error\RateLimit
+     * @throws Error\Idempotency
+     */
     private function _interpretResponse($rbody, $rcode, $rheaders)
     {
-        try {
-            $resp = json_decode($rbody, true);
-        } catch (Exception $e) {
+        $resp = json_decode($rbody, true);
+        $jsonError = json_last_error();
+        if ($resp === null && $jsonError !== JSON_ERROR_NONE) {
             $msg = "Invalid response body from API: $rbody "
-              . "(HTTP response code was $rcode)";
+              . "(HTTP response code was $rcode, json_last_error() was $jsonError)";
             throw new Error\Api($msg, $rcode, $rbody);
         }
 
         if ($rcode < 200 || $rcode >= 300) {
-            $this->handleApiError($rbody, $rcode, $rheaders, $resp);
+            $this->handleErrorResponse($rbody, $rcode, $rheaders, $resp);
         }
         return $resp;
     }
 
+    /**
+     * @static
+     *
+     * @param HttpClient\ClientInterface $client
+     */
     public static function setHttpClient($client)
     {
         self::$_httpClient = $client;
     }
 
+    /**
+     * @return HttpClient\ClientInterface
+     */
     private function httpClient()
     {
         if (!self::$_httpClient) {

+ 40 - 131
plugins/stripe/lib/ApiResource.php

@@ -9,11 +9,42 @@ namespace Stripe;
  */
 abstract class ApiResource extends StripeObject
 {
-    private static $HEADERS_TO_PERSIST = array('Stripe-Account' => true, 'Stripe-Version' => true);
+    use ApiOperations\Request;
 
-    public static function baseUrl()
+    /**
+     * @return \Stripe\Util\Set A list of fields that can be their own type of
+     * API resource (say a nested card under an account for example), and if
+     * that resource is set, it should be transmitted to the API on a create or
+     * update. Doing so is not the default behavior because API resources
+     * should normally be persisted on their own RESTful endpoints.
+     */
+    public static function getSavedNestedResources()
     {
-        return Stripe::$apiBase;
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set();
+        }
+        return $savedNestedResources;
+    }
+
+    /**
+     * @var boolean A flag that can be set a behavior that will cause this
+     * resource to be encoded and sent up along with an update of its parent
+     * resource. This is usually not desirable because resources are updated
+     * individually on their own endpoints, but there are certain cases,
+     * replacing a customer's source for example, where this is allowed.
+     */
+    public $saveWithParent = false;
+
+    public function __set($k, $v)
+    {
+        parent::__set($k, $v);
+        $v = $this->$k;
+        if ((static::getSavedNestedResources()->includes($k)) &&
+            ($v instanceof ApiResource)) {
+            $v->saveWithParent = true;
+        }
+        return $v;
     }
 
     /**
@@ -36,27 +67,11 @@ abstract class ApiResource extends StripeObject
     }
 
     /**
-     * @return string The name of the class, with namespacing and underscores
-     *    stripped.
+     * @return string The base URL for the given class.
      */
-    public static function className()
+    public static function baseUrl()
     {
-        $class = get_called_class();
-        // Useful for namespaces: Foo\Charge
-        if ($postfixNamespaces = strrchr($class, '\\')) {
-            $class = substr($postfixNamespaces, 1);
-        }
-        // Useful for underscored 'namespaces': Foo_Charge
-        if ($postfixFakeNamespaces = strrchr($class, '')) {
-            $class = $postfixFakeNamespaces;
-        }
-        if (substr($class, 0, strlen('Stripe')) == 'Stripe') {
-            $class = substr($class, strlen('Stripe'));
-        }
-        $class = str_replace('_', '', $class);
-        $name = urlencode($class);
-        $name = strtolower($name);
-        return $name;
+        return Stripe::$apiBase;
     }
 
     /**
@@ -64,7 +79,9 @@ abstract class ApiResource extends StripeObject
      */
     public static function classUrl()
     {
-        $base = static::className();
+        // Replace dots with slashes for namespaced resources, e.g. if the object's name is
+        // "foo.bar", then its URL will be "/v1/foo/bars".
+        $base = str_replace('.', '/', static::OBJECT_NAME);
         return "/v1/${base}s";
     }
 
@@ -92,112 +109,4 @@ abstract class ApiResource extends StripeObject
     {
         return static::resourceUrl($this['id']);
     }
-
-    private static function _validateParams($params = null)
-    {
-        if ($params && !is_array($params)) {
-            $message = "You must pass an array as the first argument to Stripe API "
-               . "method calls.  (HINT: an example call to create a charge "
-               . "would be: \"Stripe\\Charge::create(array('amount' => 100, "
-               . "'currency' => 'usd', 'card' => array('number' => "
-               . "4242424242424242, 'exp_month' => 5, 'exp_year' => 2015)))\")";
-            throw new Error\Api($message);
-        }
-    }
-
-    protected function _request($method, $url, $params = array(), $options = null)
-    {
-        $opts = $this->_opts->merge($options);
-        list($resp, $options) = static::_staticRequest($method, $url, $params, $opts);
-        $this->setLastResponse($resp);
-        return array($resp->json, $options);
-    }
-
-    protected static function _staticRequest($method, $url, $params, $options)
-    {
-        $opts = Util\RequestOptions::parse($options);
-        $requestor = new ApiRequestor($opts->apiKey, static::baseUrl());
-        list($response, $opts->apiKey) = $requestor->request($method, $url, $params, $opts->headers);
-        foreach ($opts->headers as $k => $v) {
-            if (!array_key_exists($k, self::$HEADERS_TO_PERSIST)) {
-                unset($opts->headers[$k]);
-            }
-        }
-        return array($response, $opts);
-    }
-
-    protected static function _retrieve($id, $options = null)
-    {
-        $opts = Util\RequestOptions::parse($options);
-        $instance = new static($id, $opts);
-        $instance->refresh();
-        return $instance;
-    }
-
-    protected static function _all($params = null, $options = null)
-    {
-        self::_validateParams($params);
-        $url = static::classUrl();
-
-        list($response, $opts) = static::_staticRequest('get', $url, $params, $options);
-        $obj = Util\Util::convertToStripeObject($response->json, $opts);
-        if (!is_a($obj, 'Stripe\\Collection')) {
-            $class = get_class($obj);
-            $message = "Expected type \"Stripe\\Collection\", got \"$class\" instead";
-            throw new Error\Api($message);
-        }
-        $obj->setLastResponse($response);
-        $obj->setRequestParams($params);
-        return $obj;
-    }
-
-    protected static function _create($params = null, $options = null)
-    {
-        self::_validateParams($params);
-        $url = static::classUrl();
-
-        list($response, $opts) = static::_staticRequest('post', $url, $params, $options);
-        $obj = Util\Util::convertToStripeObject($response->json, $opts);
-        $obj->setLastResponse($response);
-        return $obj;
-    }
-
-    /**
-     * @param string $id The ID of the API resource to update.
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ApiResource the updated API resource
-     */
-    protected static function _update($id, $params = null, $options = null)
-    {
-        self::_validateParams($params);
-        $url = static::resourceUrl($id);
-
-        list($response, $opts) = static::_staticRequest('post', $url, $params, $options);
-        $obj = Util\Util::convertToStripeObject($response->json, $opts);
-        $obj->setLastResponse($response);
-        return $obj;
-    }
-
-    protected function _save($options = null)
-    {
-        $params = $this->serializeParameters();
-        if (count($params) > 0) {
-            $url = $this->instanceUrl();
-            list($response, $opts) = $this->_request('post', $url, $params, $options);
-            $this->refreshFrom($response, $opts);
-        }
-        return $this;
-    }
-
-    protected function _delete($params = null, $options = null)
-    {
-        self::_validateParams($params);
-
-        $url = $this->instanceUrl();
-        list($response, $opts) = $this->_request('delete', $url, $params, $options);
-        $this->refreshFrom($response, $opts);
-        return $this;
-    }
 }

+ 8 - 45
plugins/stripe/lib/ApplePayDomain.php

@@ -9,7 +9,14 @@ namespace Stripe;
  */
 class ApplePayDomain extends ApiResource
 {
-    
+
+    const OBJECT_NAME = "apple_pay_domain";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+
     /**
      * @return string The class URL for this resource. It needs to be special
      *    cased because it doesn't fit into the standard resource pattern.
@@ -18,48 +25,4 @@ class ApplePayDomain extends ApiResource
     {
         return '/v1/apple_pay/domains';
     }
-
-    /**
-     * @param string $id The ID of the domain to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return ApplePayDomain
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ApplePayDomain The created domain.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ApplePayDomain The deleted domain.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of ApplePayDomains
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
 }

+ 51 - 22
plugins/stripe/lib/ApplicationFee.php

@@ -5,65 +5,94 @@ namespace Stripe;
 /**
  * Class ApplicationFee
  *
+ * @property string $id
+ * @property string $object
+ * @property string $account
+ * @property int $amount
+ * @property int $amount_refunded
+ * @property string $application
+ * @property string $balance_transaction
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property bool $livemode
+ * @property string $originating_transaction
+ * @property bool $refunded
+ * @property Collection $refunds
+ *
  * @package Stripe
  */
 class ApplicationFee extends ApiResource
 {
+
+    const OBJECT_NAME = "application_fee";
+
+    use ApiOperations\All;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+
+    const PATH_REFUNDS = '/refunds';
+
     /**
-     * This is a special case because the application fee endpoint has an
-     *    underscore in it. The parent `className` function strips underscores.
+     * @param array|null $params
+     * @param array|string|null $opts
      *
-     * @return string The name of the class.
+     * @return ApplicationFee The refunded application fee.
      */
-    public static function className()
+    public function refund($params = null, $opts = null)
     {
-        return 'application_fee';
+        $this->refunds->create($params, $opts);
+        $this->refresh();
+        return $this;
     }
 
     /**
-     * @param string $id The ID of the application fee to retrieve.
+     * @param array|null $id The ID of the application fee on which to create the refund.
+     * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return ApplicationFee
+     * @return ApplicationFeeRefund
      */
-    public static function retrieve($id, $opts = null)
+    public static function createRefund($id, $params = null, $opts = null)
     {
-        return self::_retrieve($id, $opts);
+        return self::_createNestedResource($id, static::PATH_REFUNDS, $params, $opts);
     }
 
     /**
-     * @param string $id The ID of the application fee to update.
+     * @param array|null $id The ID of the application fee to which the refund belongs.
+     * @param array|null $refundId The ID of the refund to retrieve.
      * @param array|null $params
-     * @param array|string|null $options
+     * @param array|string|null $opts
      *
-     * @return ApplicationFee The updated application fee.
+     * @return ApplicationFeeRefund
      */
-    public static function update($id, $params = null, $options = null)
+    public static function retrieveRefund($id, $refundId, $params = null, $opts = null)
     {
-        return self::_update($id, $params, $options);
+        return self::_retrieveNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the application fee to which the refund belongs.
+     * @param array|null $refundId The ID of the refund to update.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Collection of ApplicationFees
+     * @return ApplicationFeeRefund
      */
-    public static function all($params = null, $opts = null)
+    public static function updateRefund($id, $refundId, $params = null, $opts = null)
     {
-        return self::_all($params, $opts);
+        return self::_updateNestedResource($id, static::PATH_REFUNDS, $refundId, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the application fee on which to retrieve the refunds.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return ApplicationFee The refunded application fee.
+     * @return ApplicationFeeRefund
      */
-    public function refund($params = null, $opts = null)
+    public static function allRefunds($id, $params = null, $opts = null)
     {
-        $this->refunds->create($params, $opts);
-        $this->refresh();
-        return $this;
+        return self::_allNestedResources($id, static::PATH_REFUNDS, $params, $opts);
     }
 }

+ 16 - 0
plugins/stripe/lib/ApplicationFeeRefund.php

@@ -5,10 +5,26 @@ namespace Stripe;
 /**
  * Class ApplicationFeeRefund
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $fee
+ * @property StripeObject $metadata
+ *
  * @package Stripe
  */
 class ApplicationFeeRefund extends ApiResource
 {
+
+    const OBJECT_NAME = "fee_refund";
+
+    use ApiOperations\Update {
+        save as protected _save;
+    }
+
     /**
      * @return string The API URL for this Stripe refund.
      */

+ 0 - 31
plugins/stripe/lib/AttachedObject.php

@@ -1,31 +0,0 @@
-<?php
-
-namespace Stripe;
-
-/**
- * Class AttachedObject
- *
- * e.g. metadata on Stripe objects.
- *
- * @package Stripe
- */
-class AttachedObject extends StripeObject
-{
-    /**
-     * Updates this object.
-     *
-     * @param array $properties A mapping of properties to update on this object.
-     */
-    public function replaceWith($properties)
-    {
-        $removed = array_diff(array_keys($this->_values), array_keys($properties));
-        // Don't unset, but rather set to null so we send up '' for deletion.
-        foreach ($removed as $k) {
-            $this->$k = null;
-        }
-
-        foreach ($properties as $k => $v) {
-            $this->$k = $v;
-        }
-    }
-}

+ 7 - 3
plugins/stripe/lib/Balance.php

@@ -6,14 +6,18 @@ namespace Stripe;
  * Class Balance
  *
  * @property string $object
- * @property mixed $available
- * @property bool $livedmode
- * @property mixed $pending
+ * @property array $available
+ * @property array $connect_reserved
+ * @property bool $livemode
+ * @property array $pending
  *
  * @package Stripe
  */
 class Balance extends SingletonApiResource
 {
+
+    const OBJECT_NAME = "balance";
+
     /**
      * @param array|string|null $opts
      *

+ 7 - 23
plugins/stripe/lib/BalanceTransaction.php

@@ -12,11 +12,11 @@ namespace Stripe;
  * @property int $created
  * @property string $currency
  * @property string $description
+ * @property float $exchange_rate
  * @property int $fee
  * @property mixed $fee_details
  * @property int $net
  * @property string $source
- * @property mixed $sourced_transfers
  * @property string $status
  * @property string $type
  *
@@ -24,6 +24,12 @@ namespace Stripe;
  */
 class BalanceTransaction extends ApiResource
 {
+
+    const OBJECT_NAME = "balance_transaction";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
     /**
      * @return string The class URL for this resource. It needs to be special
      *    cased because it doesn't fit into the standard resource pattern.
@@ -32,26 +38,4 @@ class BalanceTransaction extends ApiResource
     {
         return "/v1/balance/history";
     }
-
-    /**
-     * @param string $id The ID of the balance transaction to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return BalanceTransaction
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of BalanceTransactions
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
 }

+ 75 - 1
plugins/stripe/lib/BankAccount.php

@@ -5,11 +5,85 @@ namespace Stripe;
 /**
  * Class BankAccount
  *
+ * @property string $id
+ * @property string $object
+ * @property string $account
+ * @property string $account_holder_name
+ * @property string $account_holder_type
+ * @property string $bank_name
+ * @property string $country
+ * @property string $currency
+ * @property string $customer
+ * @property bool $default_for_currency
+ * @property string $fingerprint
+ * @property string $last4
+ * @property StripeObject $metadata
+ * @property string $routing_number
+ * @property string $status
+ *
  * @package Stripe
  */
-class BankAccount extends ExternalAccount
+class BankAccount extends ApiResource
 {
+
+    const OBJECT_NAME = "bank_account";
+
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because it doesn't fit into the standard resource pattern.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } elseif ($this['account']) {
+            $base = Account::classUrl();
+            $parent = $this['account'];
+            $path = 'external_accounts';
+        } else {
+            $msg = "Bank accounts cannot be accessed without a customer ID or account ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " .
+               "Retrieve a bank account using \$customer->sources->retrieve('bank_account_id') or " .
+               "\$account->external_accounts->retrieve('bank_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
     /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Bank accounts cannot be accessed without a customer ID or account ID. " .
+               "Call save() on \$customer->sources->retrieve('bank_account_id') or " .
+               "\$account->external_accounts->retrieve('bank_account_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+   /**
      * @param array|null $params
      * @param array|string|null $options
      *

+ 18 - 54
plugins/stripe/lib/BitcoinReceiver.php

@@ -6,9 +6,18 @@ namespace Stripe;
  * Class BitcoinReceiver
  *
  * @package Stripe
+ *
+ * @deprecated Bitcoin receivers are deprecated. Please use the sources API instead.
+ * @link https://stripe.com/docs/sources/bitcoin
  */
-class BitcoinReceiver extends ExternalAccount
+class BitcoinReceiver extends ApiResource
 {
+
+    const OBJECT_NAME = "bitcoin_receiver";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+
     /**
      * @return string The class URL for this resource. It needs to be special
      *    cased because it doesn't fit into the standard resource pattern.
@@ -24,62 +33,17 @@ class BitcoinReceiver extends ExternalAccount
      */
     public function instanceUrl()
     {
-        $result = parent::instanceUrl();
-        if ($result) {
-            return $result;
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+            $parentExtn = urlencode(Util\Util::utf8($parent));
+            $extn = urlencode(Util\Util::utf8($this['id']));
+            return "$base/$parentExtn/$path/$extn";
         } else {
-            $id = $this['id'];
-            $id = Util\Util::utf8($id);
-            $extn = urlencode($id);
             $base = BitcoinReceiver::classUrl();
+            $extn = urlencode(Util\Util::utf8($this['id']));
             return "$base/$extn";
         }
     }
-
-    /**
-     * @param string $id The ID of the Bitcoin Receiver to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return BitcoinReceiver
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of BitcoinReceivers
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return BitcoinReceiver The created Bitcoin Receiver item.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return BitcoinReceiver The refunded Bitcoin Receiver item.
-     */
-    public function refund($params = null, $options = null)
-    {
-        $url = $this->instanceUrl() . '/refund';
-        list($response, $opts) = $this->_request('post', $url, $params, $options);
-        $this->refreshFrom($response, $opts);
-        return $this;
-    }
 }

+ 1 - 0
plugins/stripe/lib/BitcoinTransaction.php

@@ -10,4 +10,5 @@ namespace Stripe;
 class BitcoinTransaction extends ApiResource
 {
 
+    const OBJECT_NAME = "bitcoin_transaction";
 }

+ 93 - 1
plugins/stripe/lib/Card.php

@@ -5,9 +5,101 @@ namespace Stripe;
 /**
  * Class Card
  *
+ * @property string $id
+ * @property string $object
+ * @property string $account
+ * @property string $address_city
+ * @property string $address_country
+ * @property string $address_line1
+ * @property string $address_line1_check
+ * @property string $address_line2
+ * @property string $address_state
+ * @property string $address_zip
+ * @property string $address_zip_check
+ * @property array $available_payout_methods
+ * @property string $brand
+ * @property string $country
+ * @property string $currency
+ * @property string $customer
+ * @property string $cvc_check
+ * @property bool $default_for_currency
+ * @property string $dynamic_last4
+ * @property int $exp_month
+ * @property int $exp_year
+ * @property string $fingerprint
+ * @property string $funding
+ * @property string $last4
+ * @property StripeObject $metadata
+ * @property string $name
+ * @property string $recipient
+ * @property string $tokenization_method
+ *
  * @package Stripe
  */
-class Card extends ExternalAccount
+class Card extends ApiResource
 {
 
+    const OBJECT_NAME = "card";
+
+    use ApiOperations\Delete;
+    use ApiOperations\Update;
+
+    /**
+     * @return string The instance URL for this resource. It needs to be special
+     *    cased because cards are nested resources that may belong to different
+     *    top-level resources.
+     */
+    public function instanceUrl()
+    {
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parent = $this['customer'];
+            $path = 'sources';
+        } elseif ($this['account']) {
+            $base = Account::classUrl();
+            $parent = $this['account'];
+            $path = 'external_accounts';
+        } elseif ($this['recipient']) {
+            $base = Recipient::classUrl();
+            $parent = $this['recipient'];
+            $path = 'cards';
+        } else {
+            $msg = "Cards cannot be accessed without a customer ID, account ID or recipient ID.";
+            throw new Error\InvalidRequest($msg, null);
+        }
+        $parentExtn = urlencode(Util\Util::utf8($parent));
+        $extn = urlencode(Util\Util::utf8($this['id']));
+        return "$base/$parentExtn/$path/$extn";
+    }
+
+    /**
+     * @param array|string $_id
+     * @param array|string|null $_opts
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function retrieve($_id, $_opts = null)
+    {
+        $msg = "Cards cannot be accessed without a customer, recipient or account ID. " .
+               "Retrieve a card using \$customer->sources->retrieve('card_id'), " .
+               "\$recipient->cards->retrieve('card_id'), or";
+               "\$account->external_accounts->retrieve('card_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
+
+    /**
+     * @param string $_id
+     * @param array|null $_params
+     * @param array|string|null $_options
+     *
+     * @throws \Stripe\Error\InvalidRequest
+     */
+    public static function update($_id, $_params = null, $_options = null)
+    {
+        $msg = "Cards cannot be accessed without a customer, recipient or account ID. " .
+               "Call save() on \$customer->sources->retrieve('card_id'), " .
+               "\$recipient->cards->retrieve('card_id'), or";
+               "\$account->external_accounts->retrieve('card_id') instead.";
+        throw new Error\InvalidRequest($msg, null);
+    }
 }

+ 73 - 66
plugins/stripe/lib/Charge.php

@@ -9,91 +9,98 @@ namespace Stripe;
  * @property string $object
  * @property int $amount
  * @property int $amount_refunded
- * @property mixed $application_fee
+ * @property string $application
+ * @property string $application_fee
  * @property string $balance_transaction
  * @property bool $captured
  * @property int $created
  * @property string $currency
  * @property string $customer
- * @property mixed $description
- * @property mixed $destination
- * @property mixed $dispute
- * @property mixed $failure_code
- * @property mixed $failure_message
+ * @property string $description
+ * @property string $destination
+ * @property string $dispute
+ * @property string $failure_code
+ * @property string $failure_message
  * @property mixed $fraud_details
- * @property mixed $invoice
+ * @property string $invoice
  * @property bool $livemode
- * @property mixed $metadata
- * @property mixed $order
+ * @property StripeObject $metadata
+ * @property string $on_behalf_of
+ * @property string $order
+ * @property mixed $outcome
  * @property bool $paid
- * @property mixed $receipt_email
- * @property mixed $receipt_number
+ * @property string $receipt_email
+ * @property string $receipt_number
  * @property bool $refunded
- * @property mixed $refunds
+ * @property Collection $refunds
+ * @property string $review
  * @property mixed $shipping
  * @property mixed $source
- * @property mixed $source_transfer
- * @property mixed $statement_descriptor
+ * @property string $source_transfer
+ * @property string $statement_descriptor
  * @property string $status
+ * @property string $transfer
+ * @property string $transfer_group
  *
  * @package Stripe
  */
 class Charge extends ApiResource
 {
-    /**
-     * @param string $id The ID of the charge to retrieve.
-     * @param array|string|null $options
-     *
-     * @return Charge
-     */
-    public static function retrieve($id, $options = null)
-    {
-        return self::_retrieve($id, $options);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Collection of Charges
-     */
-    public static function all($params = null, $options = null)
-    {
-        return self::_all($params, $options);
-    }
+    const OBJECT_NAME = "charge";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Charge The created charge.
-     */
-    public static function create($params = null, $options = null)
-    {
-        return self::_create($params, $options);
-    }
-
-    /**
-     * @param string $id The ID of the charge to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Charge The updated charge.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
-     * @param array|string|null $options
-     *
-     * @return Charge The saved charge.
+     * Possible string representations of decline codes.
+     * These strings are applicable to the decline_code property of the \Stripe\Error\Card exception.
+     * @link https://stripe.com/docs/declines/codes
      */
-    public function save($options = null)
-    {
-        return $this->_save($options);
-    }
+    const DECLINED_APPROVE_WITH_ID                   = 'approve_with_id';
+    const DECLINED_CALL_ISSUER                       = 'call_issuer';
+    const DECLINED_CARD_NOT_SUPPORTED                = 'card_not_supported';
+    const DECLINED_CARD_VELOCITY_EXCEEDED            = 'card_velocity_exceeded';
+    const DECLINED_CURRENCY_NOT_SUPPORTED            = 'currency_not_supported';
+    const DECLINED_DO_NOT_HONOR                      = 'do_not_honor';
+    const DECLINED_DO_NOT_TRY_AGAIN                  = 'do_not_try_again';
+    const DECLINED_DUPLICATED_TRANSACTION            = 'duplicate_transaction';
+    const DECLINED_EXPIRED_CARD                      = 'expired_card';
+    const DECLINED_FRAUDULENT                        = 'fraudulent';
+    const DECLINED_GENERIC_DECLINE                   = 'generic_decline';
+    const DECLINED_INCORRECT_NUMBER                  = 'incorrect_number';
+    const DECLINED_INCORRECT_CVC                     = 'incorrect_cvc';
+    const DECLINED_INCORRECT_PIN                     = 'incorrect_pin';
+    const DECLINED_INCORRECT_ZIP                     = 'incorrect_zip';
+    const DECLINED_INSUFFICIENT_FUNDS                = 'insufficient_funds';
+    const DECLINED_INVALID_ACCOUNT                   = 'invalid_account';
+    const DECLINED_INVALID_AMOUNT                    = 'invalid_amount';
+    const DECLINED_INVALID_CVC                       = 'invalid_cvc';
+    const DECLINED_INVALID_EXPIRY_YEAR               = 'invalid_expiry_year';
+    const DECLINED_INVALID_NUMBER                    = 'invalid_number';
+    const DECLINED_INVALID_PIN                       = 'invalid_pin';
+    const DECLINED_ISSUER_NOT_AVAILABLE              = 'issuer_not_available';
+    const DECLINED_LOST_CARD                         = 'lost_card';
+    const DECLINED_NEW_ACCOUNT_INFORMATION_AVAILABLE = 'new_account_information_available';
+    const DECLINED_NO_ACTION_TAKEN                   = 'no_action_taken';
+    const DECLINED_NOT_PERMITTED                     = 'not_permitted';
+    const DECLINED_PICKUP_CARD                       = 'pickup_card';
+    const DECLINED_PIN_TRY_EXCEEDED                  = 'pin_try_exceeded';
+    const DECLINED_PROCESSING_ERROR                  = 'processing_error';
+    const DECLINED_REENTER_TRANSACTION               = 'reenter_transaction';
+    const DECLINED_RESTRICTED_CARD                   = 'restricted_card';
+    const DECLINED_REVOCATION_OF_ALL_AUTHORIZATIONS  = 'revocation_of_all_authorizations';
+    const DECLINED_REVOCATION_OF_AUTHORIZATION       = 'revocation_of_authorization';
+    const DECLINED_SECURITY_VIOLATION                = 'security_violation';
+    const DECLINED_SERVICE_NOT_ALLOWED               = 'service_not_allowed';
+    const DECLINED_STOLEN_CARD                       = 'stolen_card';
+    const DECLINED_STOP_PAYMENT_ORDER                = 'stop_payment_order';
+    const DECLINED_TESTMODE_DECLINE                  = 'testmode_decline';
+    const DECLINED_TRANSACTION_NOT_ALLOWED           = 'transaction_not_allowed';
+    const DECLINED_TRY_AGAIN_LATER                   = 'try_again_later';
+    const DECLINED_WITHDRAWAL_COUNT_LIMIT_EXCEEDED   = 'withdrawal_count_limit_exceeded';
 
     /**
      * @param array|null $params
@@ -135,7 +142,7 @@ class Charge extends ApiResource
     {
         $url = $this->instanceUrl() . '/dispute';
         list($response, $opts) = $this->_request('post', $url, $params, $options);
-        $this->refreshFrom(array('dispute' => $response), $opts, true);
+        $this->refreshFrom(['dispute' => $response], $opts, true);
         return $this->dispute;
     }
 
@@ -161,7 +168,7 @@ class Charge extends ApiResource
      */
     public function markAsFraudulent($opts = null)
     {
-        $params = array('fraud_details' => array('user_report' => 'fraudulent'));
+        $params = ['fraud_details' => ['user_report' => 'fraudulent']];
         $url = $this->instanceUrl();
         list($response, $opts) = $this->_request('post', $url, $params, $opts);
         $this->refreshFrom($response, $opts);
@@ -175,7 +182,7 @@ class Charge extends ApiResource
      */
     public function markAsSafe($opts = null)
     {
-        $params = array('fraud_details' => array('user_report' => 'safe'));
+        $params = ['fraud_details' => ['user_report' => 'safe']];
         $url = $this->instanceUrl();
         list($response, $opts) = $this->_request('post', $url, $params, $opts);
         $this->refreshFrom($response, $opts);

+ 19 - 7
plugins/stripe/lib/Collection.php

@@ -12,9 +12,22 @@ namespace Stripe;
  *
  * @package Stripe
  */
-class Collection extends ApiResource
+class Collection extends StripeObject
 {
-    protected $_requestParams = array();
+
+    const OBJECT_NAME = "list";
+
+    use ApiOperations\Request;
+
+    protected $_requestParams = [];
+
+    /**
+     * @return string The base URL for the given class.
+     */
+    public static function baseUrl()
+    {
+        return Stripe::$apiBase;
+    }
 
     public function setRequestParams($params)
     {
@@ -56,7 +69,7 @@ class Collection extends ApiResource
     }
 
     /**
-     * @return AutoPagingIterator An iterator that can be used to iterate
+     * @return Util\AutoPagingIterator An iterator that can be used to iterate
      *    across all objects across all pages. As page boundaries are
      *    encountered, the next page will be fetched automatically for
      *    continued iteration.
@@ -76,12 +89,11 @@ class Collection extends ApiResource
         if (isset($url['query'])) {
             // If the URL contains a query param, parse it out into $params so they
             // don't interact weirdly with each other.
-            $query = array();
+            $query = [];
             parse_str($url['query'], $query);
-            // PHP 5.2 doesn't support the ?: operator :(
-            $params = array_merge($params ? $params : array(), $query);
+            $params = array_merge($params ?: [], $query);
         }
 
-        return array($url['path'], $params);
+        return [$url['path'], $params];
     }
 }

+ 11 - 30
plugins/stripe/lib/CountrySpec.php

@@ -5,40 +5,21 @@ namespace Stripe;
 /**
  * Class CountrySpec
  *
+ * @property string $id
+ * @property string $object
+ * @property string $default_currency
+ * @property mixed $supported_bank_account_currencies
+ * @property string[] $supported_payment_currencies
+ * @property string[] $supported_payment_methods
+ * @property mixed $verification_fields
+ *
  * @package Stripe
  */
 class CountrySpec extends ApiResource
 {
-    /**
-     * This is a special case because the country specs endpoint has an
-     *    underscore in it. The parent `className` function strips underscores.
-     *
-     * @return string The name of the class.
-     */
-    public static function className()
-    {
-        return 'country_spec';
-    }
 
-    /**
-     * @param string $country The ISO country code of the country we retrieve the CountrySpec for.
-     * @param array|string|null $opts
-     *
-     * @return CountrySpec
-     */
-    public static function retrieve($country, $opts = null)
-    {
-        return self::_retrieve($country, $opts);
-    }
+    const OBJECT_NAME = "country_spec";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of CountrySpecs
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
 }

+ 21 - 63
plugins/stripe/lib/Coupon.php

@@ -5,73 +5,31 @@ namespace Stripe;
 /**
  * Class Coupon
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount_off
+ * @property int $created
+ * @property string $currency
+ * @property string $duration
+ * @property int $duration_in_months
+ * @property bool $livemode
+ * @property int $max_redemptions
+ * @property StripeObject $metadata
+ * @property int $percent_off
+ * @property int $redeem_by
+ * @property int $times_redeemed
+ * @property bool $valid
+ *
  * @package Stripe
  */
 class Coupon extends ApiResource
 {
-    /**
-     * @param string $id The ID of the coupon to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Coupon
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Coupon The created coupon.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the coupon to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Coupon The updated coupon.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Coupon The deleted coupon.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Coupon The saved coupon.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
+    const OBJECT_NAME = "coupon";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Coupons
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 89 - 76
plugins/stripe/lib/Customer.php

@@ -14,10 +14,11 @@ namespace Stripe;
  * @property string $default_source
  * @property bool $delinquent
  * @property string $description
- * @property mixed $discount
+ * @property Discount $discount
  * @property string $email
+ * @property string $invoice_prefix
  * @property bool $livemode
- * @property array $metadata
+ * @property StripeObject $metadata
  * @property mixed $shipping
  * @property Collection $sources
  * @property Collection $subscriptions
@@ -26,71 +27,28 @@ namespace Stripe;
  */
 class Customer extends ApiResource
 {
-    /**
-     * @param string $id The ID of the customer to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Customer
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Customers
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Customer The created customer.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
+    const OBJECT_NAME = "customer";
 
-    /**
-     * @param string $id The ID of the customer to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Customer The updated customer.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Customer The saved customer.
-     */
-    public function save($opts = null)
+    public static function getSavedNestedResources()
     {
-        return $this->_save($opts);
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'source',
+            ]);
+        }
+        return $savedNestedResources;
     }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Customer The deleted customer.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    const PATH_SOURCES = '/sources';
 
     /**
      * @param array|null $params
@@ -99,9 +57,7 @@ class Customer extends ApiResource
      */
     public function addInvoiceItem($params = null)
     {
-        if (!$params) {
-            $params = array();
-        }
+        $params = $params ?: [];
         $params['customer'] = $this->id;
         $ii = InvoiceItem::create($params, $this->_opts);
         return $ii;
@@ -114,9 +70,7 @@ class Customer extends ApiResource
      */
     public function invoices($params = null)
     {
-        if (!$params) {
-            $params = array();
-        }
+        $params = $params ?: [];
         $params['customer'] = $this->id;
         $invoices = Invoice::all($params, $this->_opts);
         return $invoices;
@@ -129,9 +83,7 @@ class Customer extends ApiResource
      */
     public function invoiceItems($params = null)
     {
-        if (!$params) {
-            $params = array();
-        }
+        $params = $params ?: [];
         $params['customer'] = $this->id;
         $iis = InvoiceItem::all($params, $this->_opts);
         return $iis;
@@ -144,9 +96,7 @@ class Customer extends ApiResource
      */
     public function charges($params = null)
     {
-        if (!$params) {
-            $params = array();
-        }
+        $params = $params ?: [];
         $params['customer'] = $this->id;
         $charges = Charge::all($params, $this->_opts);
         return $charges;
@@ -161,7 +111,7 @@ class Customer extends ApiResource
     {
         $url = $this->instanceUrl() . '/subscription';
         list($response, $opts) = $this->_request('post', $url, $params);
-        $this->refreshFrom(array('subscription' => $response), $opts, true);
+        $this->refreshFrom(['subscription' => $response], $opts, true);
         return $this->subscription;
     }
 
@@ -174,7 +124,7 @@ class Customer extends ApiResource
     {
         $url = $this->instanceUrl() . '/subscription';
         list($response, $opts) = $this->_request('delete', $url, $params);
-        $this->refreshFrom(array('subscription' => $response), $opts, true);
+        $this->refreshFrom(['subscription' => $response], $opts, true);
         return $this->subscription;
     }
 
@@ -185,6 +135,69 @@ class Customer extends ApiResource
     {
         $url = $this->instanceUrl() . '/discount';
         list($response, $opts) = $this->_request('delete', $url);
-        $this->refreshFrom(array('discount' => null), $opts, true);
+        $this->refreshFrom(['discount' => null], $opts, true);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer on which to create the source.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function createSource($id, $params = null, $opts = null)
+    {
+        return self::_createNestedResource($id, static::PATH_SOURCES, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to retrieve.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function retrieveSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_retrieveNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function updateSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_updateNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer to which the source belongs.
+     * @param array|null $sourceId The ID of the source to delete.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function deleteSource($id, $sourceId, $params = null, $opts = null)
+    {
+        return self::_deleteNestedResource($id, static::PATH_SOURCES, $sourceId, $params, $opts);
+    }
+
+    /**
+     * @param array|null $id The ID of the customer on which to retrieve the sources.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return ApiResource
+     */
+    public static function allSources($id, $params = null, $opts = null)
+    {
+        return self::_allNestedResources($id, static::PATH_SOURCES, $params, $opts);
     }
 }

+ 21 - 0
plugins/stripe/lib/Discount.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Discount
+ *
+ * @property string $object
+ * @property Coupon $coupon
+ * @property string $customer
+ * @property int $end
+ * @property int $start
+ * @property string $subscription
+ *
+ * @package Stripe
+ */
+class Discount extends StripeObject
+{
+
+    const OBJECT_NAME = "discount";
+}

+ 32 - 38
plugins/stripe/lib/Dispute.php

@@ -8,7 +8,7 @@ namespace Stripe;
  * @property string $id
  * @property string $object
  * @property int $amount
- * @property mixed $balance_transactions
+ * @property BalanceTransaction[] $balance_transactions
  * @property string $charge
  * @property int $created
  * @property string $currency
@@ -16,7 +16,7 @@ namespace Stripe;
  * @property mixed $evidence_details
  * @property bool $is_charge_refundable
  * @property bool $livemode
- * @property mixed $metadata
+ * @property StripeObject $metadata
  * @property string $reason
  * @property string $status
  *
@@ -24,49 +24,43 @@ namespace Stripe;
  */
 class Dispute extends ApiResource
 {
-    /**
-     * @param string $id The ID of the dispute to retrieve.
-     * @param array|string|null $options
-     *
-     * @return Dispute
-     */
-    public static function retrieve($id, $options = null)
-    {
-        return self::_retrieve($id, $options);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return array An array of Disputes.
-     */
-    public static function all($params = null, $options = null)
-    {
-        return self::_all($params, $options);
-    }
+    const OBJECT_NAME = "dispute";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
-     * @param string $id The ID of the dispute to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Dispute The updated dispute.
+     * Possible string representations of dispute reasons.
+     * @link https://stripe.com/docs/api#dispute_object
      */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
+    const REASON_BANK_CANNOT_PROCESS       = 'bank_cannot_process';
+    const REASON_CREDIT_NOT_PROCESSED      = 'credit_not_processed';
+    const REASON_CUSTOMER_INITIATED        = 'customer_initiated';
+    const REASON_DEBIT_NOT_AUTHORIZED      = 'debit_not_authorized';
+    const REASON_DUPLICATE                 = 'duplicate';
+    const REASON_FRAUDULENT                = 'fraudulent';
+    const REASON_GENERAL                   = 'general';
+    const REASON_INCORRECT_ACCOUNT_DETAILS = 'incorrect_account_details';
+    const REASON_INSUFFICIENT_FUNDS        = 'insufficient_funds';
+    const REASON_PRODUCT_NOT_RECEIVED      = 'product_not_received';
+    const REASON_PRODUCT_UNACCEPTABLE      = 'product_unacceptable';
+    const REASON_SUBSCRIPTION_CANCELED     = 'subscription_canceled';
+    const REASON_UNRECOGNIZED              = 'unrecognized';
 
     /**
-     * @param array|string|null $options
-     *
-     * @return Dispute The saved charge.
+     * Possible string representations of dispute statuses.
+     * @link https://stripe.com/docs/api#dispute_object
      */
-    public function save($options = null)
-    {
-        return $this->_save($options);
-    }
+    const STATUS_CHARGE_REFUNDED        = 'charge_refunded';
+    const STATUS_LOST                   = 'lost';
+    const STATUS_NEEDS_RESPONSE         = 'needs_response';
+    const STATUS_UNDER_REVIEW           = 'under_review';
+    const STATUS_WARNING_CLOSED         = 'warning_closed';
+    const STATUS_WARNING_NEEDS_RESPONSE = 'warning_needs_response';
+    const STATUS_WARNING_UNDER_REVIEW   = 'warning_under_review';
+    const STATUS_WON                    = 'won';
 
     /**
      * @param array|string|null $options

+ 41 - 0
plugins/stripe/lib/EphemeralKey.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class EphemeralKey
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property int $expires
+ * @property bool $livemode
+ * @property string $secret
+ * @property array $associated_objects
+ *
+ * @package Stripe
+ */
+class EphemeralKey extends ApiResource
+{
+
+    const OBJECT_NAME = "ephemeral_key";
+
+    use ApiOperations\Create {
+        create as protected _create;
+    }
+    use ApiOperations\Delete;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return EphemeralKey The created key.
+     */
+    public static function create($params = null, $opts = null)
+    {
+        if (!$opts['stripe_version']) {
+            throw new \InvalidArgumentException('stripe_version must be specified to create an ephemeral key');
+        }
+        return self::_create($params, $opts);
+    }
+}

+ 9 - 0
plugins/stripe/lib/Error/Base.php

@@ -20,11 +20,20 @@ abstract class Base extends Exception
         $this->httpHeaders = $httpHeaders;
         $this->requestId = null;
 
+        // TODO: make this a proper constructor argument in the next major
+        //       release.
+        $this->stripeCode = isset($jsonBody["error"]["code"]) ? $jsonBody["error"]["code"] : null;
+
         if ($httpHeaders && isset($httpHeaders['Request-Id'])) {
             $this->requestId = $httpHeaders['Request-Id'];
         }
     }
 
+    public function getStripeCode()
+    {
+        return $this->stripeCode;
+    }
+
     public function getHttpStatus()
     {
         return $this->httpStatus;

+ 6 - 7
plugins/stripe/lib/Error/Card.php

@@ -15,12 +15,16 @@ class Card extends Base
     ) {
         parent::__construct($message, $httpStatus, $httpBody, $jsonBody, $httpHeaders);
         $this->stripeParam = $stripeParam;
+
+        // TODO: once Error\Base accepts the error code as an argument, pass it
+        //       in the call to parent::__construct() and stop setting it here.
         $this->stripeCode = $stripeCode;
 
         // This one is not like the others because it was added later and we're
         // trying to do our best not to change the public interface of this class'
-        // constructor. We should consider changing its implementation on the
-        // next major version bump of this library.
+        // constructor.
+        // TODO: make this a proper constructor argument in the next major
+        //       release.
         $this->declineCode = isset($jsonBody["error"]["decline_code"]) ? $jsonBody["error"]["decline_code"] : null;
     }
 
@@ -29,11 +33,6 @@ class Card extends Base
         return $this->declineCode;
     }
 
-    public function getStripeCode()
-    {
-        return $this->stripeCode;
-    }
-
     public function getStripeParam()
     {
         return $this->stripeParam;

+ 7 - 0
plugins/stripe/lib/Error/Idempotency.php

@@ -0,0 +1,7 @@
+<?php
+
+namespace Stripe\Error;
+
+class Idempotency extends Base
+{
+}

+ 10 - 0
plugins/stripe/lib/Error/OAuth/InvalidClient.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidClient is raised when authentication fails.
+ */
+class InvalidClient extends OAuthBase
+{
+}

+ 13 - 0
plugins/stripe/lib/Error/OAuth/InvalidGrant.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidGrant is raised when a specified code doesn't exist, is
+ * expired, has been used, or doesn't belong to you; a refresh token doesn't
+ * exist, or doesn't belong to you; or if an API key's mode (live or test)
+ * doesn't match the mode of a code or refresh token.
+ */
+class InvalidGrant extends OAuthBase
+{
+}

+ 11 - 0
plugins/stripe/lib/Error/OAuth/InvalidRequest.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidRequest is raised when a code, refresh token, or grant type
+ * parameter is not provided, but was required.
+ */
+class InvalidRequest extends OAuthBase
+{
+}

+ 10 - 0
plugins/stripe/lib/Error/OAuth/InvalidScope.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * InvalidScope is raised when an invalid scope parameter is provided.
+ */
+class InvalidScope extends OAuthBase
+{
+}

+ 23 - 0
plugins/stripe/lib/Error/OAuth/OAuthBase.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+class OAuthBase extends \Stripe\Error\Base
+{
+    public function __construct(
+        $code,
+        $description,
+        $httpStatus = null,
+        $httpBody = null,
+        $jsonBody = null,
+        $httpHeaders = null
+    ) {
+        parent::__construct($description, $httpStatus, $httpBody, $jsonBody, $httpHeaders);
+        $this->code = $code;
+    }
+
+    public function getErrorCode()
+    {
+        return $this->code;
+    }
+}

+ 11 - 0
plugins/stripe/lib/Error/OAuth/UnsupportedGrantType.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * UnsupportedGrantType is raised when an unuspported grant type
+ * parameter is specified.
+ */
+class UnsupportedGrantType extends OAuthBase
+{
+}

+ 11 - 0
plugins/stripe/lib/Error/OAuth/UnsupportedResponseType.php

@@ -0,0 +1,11 @@
+<?php
+
+namespace Stripe\Error\OAuth;
+
+/**
+ * UnsupportedResponseType is raised when an unsupported response type
+ * parameter is specified.
+ */
+class UnsupportedResponseType extends OAuthBase
+{
+}

+ 20 - 0
plugins/stripe/lib/Error/SignatureVerification.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Stripe\Error;
+
+class SignatureVerification extends Base
+{
+    public function __construct(
+        $message,
+        $sigHeader,
+        $httpBody = null
+    ) {
+        parent::__construct($message, null, $httpBody, null, null);
+        $this->sigHeader = $sigHeader;
+    }
+
+    public function getSigHeader()
+    {
+        return $this->sigHeader;
+    }
+}

+ 96 - 20
plugins/stripe/lib/Event.php

@@ -7,37 +7,113 @@ namespace Stripe;
  *
  * @property string $id
  * @property string $object
+ * @property string $account
  * @property string $api_version
  * @property int $created
  * @property mixed $data
  * @property bool $livemode
  * @property int $pending_webhooks
- * @property string $request
+ * @property mixed $request
  * @property string $type
  *
  * @package Stripe
  */
 class Event extends ApiResource
 {
-    /**
-     * @param string $id The ID of the event to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Event
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Events
+    const OBJECT_NAME = "event";
+
+     /**
+     * Possible string representations of event types.
+     * @link https://stripe.com/docs/api#event_types
      */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const ACCOUNT_UPDATED                      = 'account.updated';
+    const ACCOUNT_APPLICATION_DEAUTHORIZED     = 'account.application.deauthorized';
+    const ACCOUNT_EXTERNAL_ACCOUNT_CREATED     = 'account.external_account.created';
+    const ACCOUNT_EXTERNAL_ACCOUNT_DELETED     = 'account.external_account.deleted';
+    const ACCOUNT_EXTERNAL_ACCOUNT_UPDATED     = 'account.external_account.updated';
+    const APPLICATION_FEE_CREATED              = 'application_fee.created';
+    const APPLICATION_FEE_REFUNDED             = 'application_fee.refunded';
+    const APPLICATION_FEE_REFUND_UPDATED       = 'application_fee.refund.updated';
+    const BALANCE_AVAILABLE                    = 'balance.available';
+    const CHARGE_CAPTURED                      = 'charge.captured';
+    const CHARGE_EXPIRED                       = 'charge.expired';
+    const CHARGE_FAILED                        = 'charge.failed';
+    const CHARGE_PENDING                       = 'charge.pending';
+    const CHARGE_REFUNDED                      = 'charge.refunded';
+    const CHARGE_SUCCEEDED                     = 'charge.succeeded';
+    const CHARGE_UPDATED                       = 'charge.updated';
+    const CHARGE_DISPUTE_CLOSED                = 'charge.dispute.closed';
+    const CHARGE_DISPUTE_CREATED               = 'charge.dispute.created';
+    const CHARGE_DISPUTE_FUNDS_REINSTATED      = 'charge.dispute.funds_reinstated';
+    const CHARGE_DISPUTE_FUNDS_WITHDRAWN       = 'charge.dispute.funds_withdrawn';
+    const CHARGE_DISPUTE_UPDATED               = 'charge.dispute.updated';
+    const CHARGE_REFUND_UPDATED                = 'charge.refund.updated';
+    const COUPON_CREATED                       = 'coupon.created';
+    const COUPON_DELETED                       = 'coupon.deleted';
+    const COUPON_UPDATED                       = 'coupon.updated';
+    const CUSTOMER_CREATED                     = 'customer.created';
+    const CUSTOMER_DELETED                     = 'customer.deleted';
+    const CUSTOMER_UPDATED                     = 'customer.updated';
+    const CUSTOMER_DISCOUNT_CREATED            = 'customer.discount.created';
+    const CUSTOMER_DISCOUNT_DELETED            = 'customer.discount.deleted';
+    const CUSTOMER_DISCOUNT_UPDATED            = 'customer.discount.updated';
+    const CUSTOMER_SOURCE_CREATED              = 'customer.source.created';
+    const CUSTOMER_SOURCE_DELETED              = 'customer.source.deleted';
+    const CUSTOMER_SOURCE_EXPIRING             = 'customer.source.expiring';
+    const CUSTOMER_SOURCE_UPDATED              = 'customer.source.updated';
+    const CUSTOMER_SUBSCRIPTION_CREATED        = 'customer.subscription.created';
+    const CUSTOMER_SUBSCRIPTION_DELETED        = 'customer.subscription.deleted';
+    const CUSTOMER_SUBSCRIPTION_TRIAL_WILL_END = 'customer.subscription.trial_will_end';
+    const CUSTOMER_SUBSCRIPTION_UPDATED        = 'customer.subscription.updated';
+    const FILE_CREATED                         = 'file.created';
+    const INVOICE_CREATED                      = 'invoice.created';
+    const INVOICE_PAYMENT_FAILED               = 'invoice.payment_failed';
+    const INVOICE_PAYMENT_SUCCEEDED            = 'invoice.payment_succeeded';
+    const INVOICE_SENT                         = 'invoice.sent';
+    const INVOICE_UPCOMING                     = 'invoice.upcoming';
+    const INVOICE_UPDATED                      = 'invoice.updated';
+    const INVOICEITEM_CREATED                  = 'invoiceitem.created';
+    const INVOICEITEM_DELETED                  = 'invoiceitem.deleted';
+    const INVOICEITEM_UPDATED                  = 'invoiceitem.updated';
+    const ORDER_CREATED                        = 'order.created';
+    const ORDER_PAYMENT_FAILED                 = 'order.payment_failed';
+    const ORDER_PAYMENT_SUCCEEDED              = 'order.payment_succeeded';
+    const ORDER_UPDATED                        = 'order.updated';
+    const ORDER_RETURN_CREATED                 = 'order_return.created';
+    const PAYOUT_CANCELED                      = 'payout.canceled';
+    const PAYOUT_CREATED                       = 'payout.created';
+    const PAYOUT_FAILED                        = 'payout.failed';
+    const PAYOUT_PAID                          = 'payout.paid';
+    const PAYOUT_UPDATED                       = 'payout.updated';
+    const PING                                 = 'ping';
+    const PLAN_CREATED                         = 'plan.created';
+    const PLAN_DELETED                         = 'plan.deleted';
+    const PLAN_UPDATED                         = 'plan.updated';
+    const PRODUCT_CREATED                      = 'product.created';
+    const PRODUCT_DELETED                      = 'product.deleted';
+    const PRODUCT_UPDATED                      = 'product.updated';
+    const RECIPIENT_CREATED                    = 'recipient.created';
+    const RECIPIENT_DELETED                    = 'recipient.deleted';
+    const RECIPIENT_UPDATED                    = 'recipient.updated';
+    const REVIEW_CLOSED                        = 'review.closed';
+    const REVIEW_OPENED                        = 'review.opened';
+    const SIGMA_SCHEDULED_QUERY_RUN_CREATED    = 'sigma.scheduled_query_run.created';
+    const SKU_CREATED                          = 'sku.created';
+    const SKU_DELETED                          = 'sku.deleted';
+    const SKU_UPDATED                          = 'sku.updated';
+    const SOURCE_CANCELED                      = 'source.canceled';
+    const SOURCE_CHARGEABLE                    = 'source.chargeable';
+    const SOURCE_FAILED                        = 'source.failed';
+    const SOURCE_MANDATE_NOTIFICATION          = 'source.mandate_notification';
+    const SOURCE_TRANSACTION_CREATED           = 'source.transaction.created';
+    const TOPUP_CREATED                        = 'topup.created';
+    const TOPUP_FAILED                         = 'topup.failed';
+    const TOPUP_SUCCEEDED                      = 'topup.succeeded';
+    const TRANSFER_CREATED                     = 'transfer.created';
+    const TRANSFER_REVERSED                    = 'transfer.reversed';
+    const TRANSFER_UPDATED                     = 'transfer.updated';
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
 }

+ 17 - 0
plugins/stripe/lib/ExchangeRate.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class ExchangeRate
+ *
+ * @package Stripe
+ */
+class ExchangeRate extends ApiResource
+{
+
+    const OBJECT_NAME = "exchange_rate";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+}

+ 0 - 89
plugins/stripe/lib/ExternalAccount.php

@@ -1,89 +0,0 @@
-<?php
-
-namespace Stripe;
-
-/**
- * Class ExternalAccount
- *
- * @package Stripe
- */
-abstract class ExternalAccount extends ApiResource
-{
-    /**
-     * @return string The instance URL for this resource. It needs to be special
-     *    cased because it doesn't fit into the standard resource pattern.
-     */
-    public function instanceUrl()
-    {
-        $id = $this['id'];
-        if (!$id) {
-            $class = get_class($this);
-            $msg = "Could not determine which URL to request: $class instance "
-             . "has invalid ID: $id";
-            throw new Error\InvalidRequest($msg, null);
-        }
-
-        if ($this['customer']) {
-            $parent = $this['customer'];
-            $base = Customer::classUrl();
-            $path = 'sources';
-        } elseif ($this['account']) {
-            $parent = $this['account'];
-            $base = Account::classUrl();
-            $path = 'external_accounts';
-        } elseif ($this['recipient']) {
-            $parent = $this['recipient'];
-            $base = Recipient::classUrl();
-            $path = 'cards';
-        } else {
-            return null;
-        }
-
-        $parent = Util\Util::utf8($parent);
-        $id = Util\Util::utf8($id);
-
-        $parentExtn = urlencode($parent);
-        $extn = urlencode($id);
-        return "$base/$parentExtn/$path/$extn";
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ExternalAccount The deleted external account.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return ExternalAccount The saved external account.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ExternalAccount The verified (or not) external account.
-     */
-    public function verify($params = null, $opts = null)
-    {
-        if ($this['customer']) {
-            $url = $this->instanceUrl() . '/verify';
-            list($response, $options) = $this->_request('post', $url, $params, $opts);
-            $this->refreshFrom($response, $options);
-            return $this;
-        } else {
-            $message = 'Only customer external accounts can be verified in this manner.';
-            throw new Error\Api($message);
-        }
-    }
-}

+ 30 - 0
plugins/stripe/lib/FileLink.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class FileLink
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property bool $expired
+ * @property int $expires_at
+ * @property string $file
+ * @property string $purpose
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $url
+ *
+ * @package Stripe
+ */
+class FileLink extends ApiResource
+{
+
+    const OBJECT_NAME = "file_link";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+}

+ 10 - 34
plugins/stripe/lib/FileUpload.php

@@ -8,54 +8,30 @@ namespace Stripe;
  * @property string $id
  * @property string $object
  * @property int $created
+ * @property string $filename
  * @property string $purpose
  * @property int $size
  * @property string $type
+ * @property string $url
  *
  * @package Stripe
  */
 class FileUpload extends ApiResource
 {
-    public static function baseUrl()
-    {
-        return Stripe::$apiUploadBase;
-    }
 
-    public static function className()
-    {
-        return 'file';
-    }
+    const OBJECT_NAME = "file_upload";
 
-    /**
-     * @param string $id The ID of the file upload to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return FileUpload
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return FileUpload The created file upload.
-     */
-    public static function create($params = null, $opts = null)
+    public static function baseUrl()
     {
-        return self::_create($params, $opts);
+        return Stripe::$apiUploadBase;
     }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of FileUploads
-     */
-    public static function all($params = null, $opts = null)
+    public static function classUrl()
     {
-        return self::_all($params, $opts);
+        return '/v1/files';
     }
 }

+ 5 - 2
plugins/stripe/lib/HttpClient/ClientInterface.php

@@ -11,8 +11,11 @@ interface ClientInterface
      * @param array $params KV pairs for parameters. Can be nested for arrays and hashes
      * @param boolean $hasFile Whether or not $params references a file (via an @ prefix or
      *                         CurlFile)
-     * @throws Error\Api & Error\ApiConnection
-     * @return array($rawBody, $httpStatusCode, $httpHeader)
+     *
+     * @throws \Stripe\Error\Api
+     * @throws \Stripe\Error\ApiConnection
+     * @return array An array whose first element is raw request body, second
+     *    element is HTTP status code and third array of HTTP headers.
      */
     public function request($method, $absUrl, $headers, $params, $hasFile);
 }

+ 128 - 69
plugins/stripe/lib/HttpClient/CurlClient.php

@@ -22,6 +22,10 @@ if (!defined('CURL_SSLVERSION_TLSv1_2')) {
 }
 // @codingStandardsIgnoreEnd
 
+if (!defined('CURL_HTTP_VERSION_2TLS')) {
+    define('CURL_HTTP_VERSION_2TLS', 4);
+}
+
 class CurlClient implements ClientInterface
 {
     private static $instance;
@@ -36,6 +40,8 @@ class CurlClient implements ClientInterface
 
     protected $defaultOptions;
 
+    protected $userAgentInfo;
+
     /**
      * CurlClient constructor.
      *
@@ -49,9 +55,20 @@ class CurlClient implements ClientInterface
      *
      * @param array|callable|null $defaultOptions
      */
-    public function __construct($defaultOptions = null)
+    public function __construct($defaultOptions = null, $randomGenerator = null)
     {
         $this->defaultOptions = $defaultOptions;
+        $this->randomGenerator = $randomGenerator ?: new Util\RandomGenerator();
+        $this->initUserAgentInfo();
+    }
+
+    public function initUserAgentInfo()
+    {
+        $curlVersion = curl_version();
+        $this->userAgentInfo = [
+            'httplib' =>  'curl ' . $curlVersion['version'],
+            'ssllib' => $curlVersion['ssl_version'],
+        ];
     }
 
     public function getDefaultOptions()
@@ -59,6 +76,11 @@ class CurlClient implements ClientInterface
         return $this->defaultOptions;
     }
 
+    public function getUserAgentInfo()
+    {
+        return $this->userAgentInfo;
+    }
+
     // USER DEFINED TIMEOUTS
 
     const DEFAULT_TIMEOUT = 80;
@@ -93,10 +115,9 @@ class CurlClient implements ClientInterface
 
     public function request($method, $absUrl, $headers, $params, $hasFile)
     {
-        $curl = curl_init();
         $method = strtolower($method);
 
-        $opts = array();
+        $opts = [];
         if (is_callable($this->defaultOptions)) { // call defaultOptions callback, set options to return value
             $opts = call_user_func_array($this->defaultOptions, func_get_args());
             if (!is_array($opts)) {
@@ -114,24 +135,32 @@ class CurlClient implements ClientInterface
             }
             $opts[CURLOPT_HTTPGET] = 1;
             if (count($params) > 0) {
-                $encoded = self::encode($params);
+                $encoded = Util\Util::urlEncode($params);
                 $absUrl = "$absUrl?$encoded";
             }
         } elseif ($method == 'post') {
             $opts[CURLOPT_POST] = 1;
-            $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : self::encode($params);
+            $opts[CURLOPT_POSTFIELDS] = $hasFile ? $params : Util\Util::urlEncode($params);
         } elseif ($method == 'delete') {
             $opts[CURLOPT_CUSTOMREQUEST] = 'DELETE';
             if (count($params) > 0) {
-                $encoded = self::encode($params);
+                $encoded = Util\Util::urlEncode($params);
                 $absUrl = "$absUrl?$encoded";
             }
         } else {
             throw new Error\Api("Unrecognized method $method");
         }
 
+        // It is only safe to retry network failures on POST requests if we
+        // add an Idempotency-Key header
+        if (($method == 'post') && (Stripe::$maxNetworkRetries > 0)) {
+            if (!isset($headers['Idempotency-Key'])) {
+                array_push($headers, 'Idempotency-Key: ' . $this->randomGenerator->uuid());
+            }
+        }
+
         // Create a callback to capture HTTP headers for the response
-        $rheaders = array();
+        $rheaders = [];
         $headerCallback = function ($curl, $header_line) use (&$rheaders) {
             // Ignore the HTTP request line (HTTP/1.1 200 OK)
             if (strpos($header_line, ":") === false) {
@@ -163,50 +192,66 @@ class CurlClient implements ClientInterface
         $opts[CURLOPT_TIMEOUT] = $this->timeout;
         $opts[CURLOPT_HEADERFUNCTION] = $headerCallback;
         $opts[CURLOPT_HTTPHEADER] = $headers;
-        if (!Stripe::$verifySslCerts) {
+        $opts[CURLOPT_CAINFO] = Stripe::getCABundlePath();
+        if (!Stripe::getVerifySslCerts()) {
             $opts[CURLOPT_SSL_VERIFYPEER] = false;
         }
 
-        curl_setopt_array($curl, $opts);
-        $rbody = curl_exec($curl);
+        // For HTTPS requests, enable HTTP/2, if supported
+        $opts[CURLOPT_HTTP_VERSION] = CURL_HTTP_VERSION_2TLS;
 
-        if (!defined('CURLE_SSL_CACERT_BADFILE')) {
-            define('CURLE_SSL_CACERT_BADFILE', 77);  // constant not defined in PHP
-        }
+        list($rbody, $rcode) = $this->executeRequestWithRetries($opts, $absUrl);
+
+        return [$rbody, $rcode, $rheaders];
+    }
 
-        $errno = curl_errno($curl);
-        if ($errno == CURLE_SSL_CACERT ||
-            $errno == CURLE_SSL_PEER_CERTIFICATE ||
-            $errno == CURLE_SSL_CACERT_BADFILE
-        ) {
-            array_push(
-                $headers,
-                'X-Stripe-Client-Info: {"ca":"using Stripe-supplied CA bundle"}'
-            );
-            $cert = self::caBundle();
-            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
-            curl_setopt($curl, CURLOPT_CAINFO, $cert);
+    /**
+     * @param array $opts cURL options
+     */
+    private function executeRequestWithRetries($opts, $absUrl)
+    {
+        $numRetries = 0;
+
+        while (true) {
+            $rcode = 0;
+            $errno = 0;
+
+            $curl = curl_init();
+            curl_setopt_array($curl, $opts);
             $rbody = curl_exec($curl);
+
+            if ($rbody === false) {
+                $errno = curl_errno($curl);
+                $message = curl_error($curl);
+            } else {
+                $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
+            }
+            curl_close($curl);
+
+            if ($this->shouldRetry($errno, $rcode, $numRetries)) {
+                $numRetries += 1;
+                $sleepSeconds = $this->sleepTime($numRetries);
+                usleep(intval($sleepSeconds * 1000000));
+            } else {
+                break;
+            }
         }
 
         if ($rbody === false) {
-            $errno = curl_errno($curl);
-            $message = curl_error($curl);
-            curl_close($curl);
-            $this->handleCurlError($absUrl, $errno, $message);
+            $this->handleCurlError($absUrl, $errno, $message, $numRetries);
         }
 
-        $rcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-        curl_close($curl);
-        return array($rbody, $rcode, $rheaders);
+        return [$rbody, $rcode];
     }
 
     /**
-     * @param number $errno
+     * @param string $url
+     * @param int $errno
      * @param string $message
+     * @param int $numRetries
      * @throws Error\ApiConnection
      */
-    private function handleCurlError($url, $errno, $message)
+    private function handleCurlError($url, $errno, $message, $numRetries)
     {
         switch ($errno) {
             case CURLE_COULDNT_CONNECT:
@@ -231,52 +276,66 @@ class CurlClient implements ClientInterface
         $msg .= " let us know at support@stripe.com.";
 
         $msg .= "\n\n(Network error [errno $errno]: $message)";
-        throw new Error\ApiConnection($msg);
-    }
 
-    private static function caBundle()
-    {
-        return dirname(__FILE__) . '/../../data/ca-certificates.crt';
+        if ($numRetries > 0) {
+            $msg .= "\n\nRequest was retried $numRetries times.";
+        }
+
+        throw new Error\ApiConnection($msg);
     }
 
     /**
-     * @param array $arr An map of param keys to values.
-     * @param string|null $prefix
-     *
-     * Only public for testability, should not be called outside of CurlClient
-     *
-     * @return string A querystring, essentially.
+     * Checks if an error is a problem that we should retry on. This includes both
+     * socket errors that may represent an intermittent problem and some special
+     * HTTP statuses.
+     * @param int $errno
+     * @param int $rcode
+     * @param int $numRetries
+     * @return bool
      */
-    public static function encode($arr, $prefix = null)
+    private function shouldRetry($errno, $rcode, $numRetries)
     {
-        if (!is_array($arr)) {
-            return $arr;
+        if ($numRetries >= Stripe::getMaxNetworkRetries()) {
+            return false;
         }
 
-        $r = array();
-        foreach ($arr as $k => $v) {
-            if (is_null($v)) {
-                continue;
-            }
+        // Retry on timeout-related problems (either on open or read).
+        if ($errno === CURLE_OPERATION_TIMEOUTED) {
+            return true;
+        }
 
-            if ($prefix) {
-                if ($k !== null && (!is_int($k) || is_array($v))) {
-                    $k = $prefix."[".$k."]";
-                } else {
-                    $k = $prefix."[]";
-                }
-            }
+        // Destination refused the connection, the connection was reset, or a
+        // variety of other connection failures. This could occur from a single
+        // saturated server, so retry in case it's intermittent.
+        if ($errno === CURLE_COULDNT_CONNECT) {
+            return true;
+        }
 
-            if (is_array($v)) {
-                $enc = self::encode($v, $k);
-                if ($enc) {
-                    $r[] = $enc;
-                }
-            } else {
-                $r[] = urlencode($k)."=".urlencode($v);
-            }
+        // 409 conflict
+        if ($rcode === 409) {
+            return true;
         }
 
-        return implode("&", $r);
+        return false;
+    }
+
+    private function sleepTime($numRetries)
+    {
+        // Apply exponential backoff with $initialNetworkRetryDelay on the
+        // number of $numRetries so far as inputs. Do not allow the number to exceed
+        // $maxNetworkRetryDelay.
+        $sleepSeconds = min(
+            Stripe::getInitialNetworkRetryDelay() * 1.0 * pow(2, $numRetries - 1),
+            Stripe::getMaxNetworkRetryDelay()
+        );
+
+        // Apply some jitter by randomizing the value in the range of
+        // ($sleepSeconds / 2) to ($sleepSeconds).
+        $sleepSeconds *= 0.5 * (1 + $this->randomGenerator->randFloat());
+
+        // But never sleep less than the base sleep seconds.
+        $sleepSeconds = max(Stripe::getInitialNetworkRetryDelay(), $sleepSeconds);
+
+        return $sleepSeconds;
     }
 }

+ 49 - 54
plugins/stripe/lib/Invoice.php

@@ -5,54 +5,59 @@ namespace Stripe;
 /**
  * Class Invoice
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount_due
+ * @property int $amount_paid
+ * @property int $amount_remaining
+ * @property int $application_fee
+ * @property int $attempt_count
+ * @property bool $auto_advance
+ * @property bool $attempted
+ * @property string $billing
+ * @property string $billing_reason
+ * @property string $charge
+ * @property bool $closed
+ * @property string $currency
+ * @property string $customer
+ * @property int $date
+ * @property string $description
+ * @property Discount $discount
+ * @property int $due_date
+ * @property int $ending_balance
+ * @property bool $forgiven
+ * @property string $hosted_invoice_url
+ * @property string $invoice_pdf
+ * @property Collection $lines
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property int $next_payment_attempt
+ * @property string $number
+ * @property bool $paid
+ * @property int $period_end
+ * @property int $period_start
+ * @property string $receipt_number
+ * @property int $starting_balance
+ * @property string $statement_descriptor
+ * @property string $subscription
+ * @property int $subscription_proration_date
+ * @property int $subtotal
+ * @property int $tax
+ * @property float $tax_percent
+ * @property int $total
+ * @property int $webhooks_delivered_at
+ *
  * @package Stripe
  */
 class Invoice extends ApiResource
 {
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Invoice The created invoice.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
 
-    /**
-     * @param string $id The ID of the invoice to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Invoice
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
+    const OBJECT_NAME = "invoice";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Invoices
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the invoice to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Invoice The updated invoice.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
      * @param array|null $params
@@ -69,23 +74,13 @@ class Invoice extends ApiResource
         return $obj;
     }
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Invoice The saved invoice.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
     /**
      * @return Invoice The paid invoice.
      */
-    public function pay($opts = null)
+    public function pay($params = null, $opts = null)
     {
         $url = $this->instanceUrl() . '/pay';
-        list($response, $opts) = $this->_request('post', $url, null, $opts);
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
         $this->refreshFrom($response, $opts);
         return $this;
     }

+ 25 - 63
plugins/stripe/lib/InvoiceItem.php

@@ -5,73 +5,35 @@ namespace Stripe;
 /**
  * Class InvoiceItem
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $currency
+ * @property string $customer
+ * @property int $date
+ * @property string $description
+ * @property bool $discountable
+ * @property string $invoice
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $period
+ * @property Plan $plan
+ * @property bool $proration
+ * @property int $quantity
+ * @property string $subscription
+ * @property string $subscription_item
+ * @property int $unit_amount
+ *
  * @package Stripe
  */
 class InvoiceItem extends ApiResource
 {
-    /**
-     * @param string $id The ID of the invoice item to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return InvoiceItem
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of InvoiceItems
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return InvoiceItem The created invoice item.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the invoice item to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return InvoiceItem The updated invoice item.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return InvoiceItem The saved invoice item.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
+    const OBJECT_NAME = "invoiceitem";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return InvoiceItem The deleted invoice item.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 30 - 0
plugins/stripe/lib/InvoiceLineItem.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class InvoiceLineItem
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $currency
+ * @property string $description
+ * @property bool $discountable
+ * @property string $invoice_item
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $period
+ * @property Plan $plan
+ * @property bool $proration
+ * @property int $quantity
+ * @property string $subscription
+ * @property string $subscription_item
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class InvoiceLineItem extends ApiResource
+{
+    const OBJECT_NAME = "line_item";
+}

+ 25 - 0
plugins/stripe/lib/IssuerFraudRecord.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class IssuerFraudRecord
+ *
+ * @property string $id
+ * @property string $object
+ * @property string $charge
+ * @property int $created
+ * @property int $post_date
+ * @property string $fraud_type
+ * @property bool $livemode
+ *
+ * @package Stripe
+ */
+class IssuerFraudRecord extends ApiResource
+{
+
+    const OBJECT_NAME = "issuer_fraud_record";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
+}

+ 69 - 0
plugins/stripe/lib/Issuing/Authorization.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class Authorization
+ *
+ * @property string $id
+ * @property string $object
+ * @property bool $approved
+ * @property string $authorization_method
+ * @property int $authorized_amount
+ * @property string $authorized_currency
+ * @property \Stripe\Collection $balance_transactions
+ * @property Card $card
+ * @property Cardholder $cardholder
+ * @property int $created
+ * @property int $held_amount
+ * @property string $held_currency
+ * @property bool $is_held_amount_controllable
+ * @property bool $livemode
+ * @property mixed $merchant_data
+ * @property \Stripe\StripeObject $metadata
+ * @property int $pending_authorized_amount
+ * @property int $pending_held_amount
+ * @property mixed $request_history
+ * @property string $status
+ * @property mixed $request_history
+ * @property \Stripe\Collection $transactions
+ * @property mixed $verification_data
+ *
+ * @package Stripe\Issuing
+ */
+class Authorization extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.authorization";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Retrieve;
+    use \Stripe\ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Authorization The approved authorization.
+     */
+    public function approve($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/approve';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Authorization The declined authorization.
+     */
+    public function decline($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/decline';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}

+ 51 - 0
plugins/stripe/lib/Issuing/Card.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class Card
+ *
+ * @property string $id
+ * @property string $object
+ * @property mixed $authorization_controls
+ * @property mixed $billing
+ * @property string $brand
+ * @property Cardholder $cardholder
+ * @property int $created
+ * @property string $currency
+ * @property int $exp_month
+ * @property int $exp_year
+ * @property string $last4
+ * @property bool $livemode
+ * @property \Stripe\StripeObject $metadata
+ * @property string $name
+ * @property mixed $shipping
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe\Issuing
+ */
+class Card extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.card";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Create;
+    use \Stripe\ApiOperations\Retrieve;
+    use \Stripe\ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return CardDetails The card details associated with that issuing card.
+     */
+    public function details($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/details';
+        list($response, $opts) = $this->_request('get', $url, $params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+}

+ 21 - 0
plugins/stripe/lib/Issuing/CardDetails.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class CardDetails
+ *
+ * @property string $id
+ * @property string $object
+ * @property Card $card
+ * @property string $cvc
+ * @property int $exp_month
+ * @property int $exp_year
+ * @property string $number
+ *
+ * @package Stripe\Issuing
+ */
+class CardDetails extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.card_details";
+}

+ 30 - 0
plugins/stripe/lib/Issuing/Cardholder.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class Cardholder
+ *
+ * @property string $id
+ * @property string $object
+ * @property mixed $billing
+ * @property int $created
+ * @property string $email
+ * @property bool $livemode
+ * @property \Stripe\StripeObject $metadata
+ * @property string $name
+ * @property string $phone_number
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe\Issuing
+ */
+class Cardholder extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.cardholder";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Create;
+    use \Stripe\ApiOperations\Retrieve;
+    use \Stripe\ApiOperations\Update;
+}

+ 30 - 0
plugins/stripe/lib/Issuing/Dispute.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class Dispute
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $created
+ * @property string $currency
+ * @property mixed $evidence
+ * @property bool $livemode
+ * @property \Stripe\StripeObject $metadata
+ * @property string $reason
+ * @property string $status
+ * @property Transaction $transaction
+ *
+ * @package Stripe\Issuing
+ */
+class Dispute extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.dispute";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Create;
+    use \Stripe\ApiOperations\Retrieve;
+    use \Stripe\ApiOperations\Update;
+}

+ 33 - 0
plugins/stripe/lib/Issuing/Transaction.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Stripe\Issuing;
+
+/**
+ * Class Transaction
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $authorization
+ * @property string $balance_transaction
+ * @property string $card
+ * @property string $cardholder
+ * @property int $created
+ * @property string $currency
+ * @property string $dispute
+ * @property bool $livemode
+ * @property mixed $merchant_data
+ * @property \Stripe\StripeObject $metadata
+ * @property string $type
+ *
+ * @package Stripe\Issuing
+ */
+class Transaction extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "issuing.transaction";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Create;
+    use \Stripe\ApiOperations\Retrieve;
+    use \Stripe\ApiOperations\Update;
+}

+ 0 - 18
plugins/stripe/lib/JsonSerializable.php

@@ -1,18 +0,0 @@
-<?php
-
-namespace Stripe;
-
-// JsonSerializable only exists in PHP 5.4+. Stub if out if it doesn't exist
-if (interface_exists('\JsonSerializable', false)) {
-    interface JsonSerializable extends \JsonSerializable
-    {
-    }
-} else {
-    // PSR2 wants each interface to have its own file.
-    // @codingStandardsIgnoreStart
-    interface JsonSerializable
-    {
-        // @codingStandardsIgnoreEnd
-        public function jsonSerialize();
-    }
-}

+ 18 - 0
plugins/stripe/lib/LoginLink.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class LoginLink
+ *
+ * @property string $object
+ * @property int $created
+ * @property string $url
+ *
+ * @package Stripe
+ */
+class LoginLink extends ApiResource
+{
+
+    const OBJECT_NAME = "login_link";
+}

+ 93 - 0
plugins/stripe/lib/OAuth.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Stripe;
+
+abstract class OAuth
+{
+    /**
+     * Generates a URL to Stripe's OAuth form.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return string The URL to Stripe's OAuth form.
+     */
+    public static function authorizeUrl($params = null, $opts = null)
+    {
+        $params = $params ?: [];
+
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+
+        $params['client_id'] = self::_getClientId($params);
+        if (!array_key_exists('response_type', $params)) {
+            $params['response_type'] = 'code';
+        }
+        $query = Util\Util::urlEncode($params);
+
+        return $base . '/oauth/authorize?' . $query;
+    }
+
+    /**
+     * Use an authoriztion code to connect an account to your platform and
+     * fetch the user's credentials.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public static function token($params = null, $opts = null)
+    {
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+        $requestor = new ApiRequestor(null, $base);
+        list($response, $apiKey) = $requestor->request(
+            'post',
+            '/oauth/token',
+            $params,
+            null
+        );
+        return Util\Util::convertToStripeObject($response->json, $opts);
+    }
+
+    /**
+     * Disconnects an account from your platform.
+     *
+     * @param array|null $params
+     * @param array|null $opts
+     *
+     * @return StripeObject Object containing the response from the API.
+     */
+    public static function deauthorize($params = null, $opts = null)
+    {
+        $params = $params ?: [];
+        $base = ($opts && array_key_exists('connect_base', $opts)) ? $opts['connect_base'] : Stripe::$connectBase;
+        $requestor = new ApiRequestor(null, $base);
+        $params['client_id'] = self::_getClientId($params);
+        list($response, $apiKey) = $requestor->request(
+            'post',
+            '/oauth/deauthorize',
+            $params,
+            null
+        );
+        return Util\Util::convertToStripeObject($response->json, $opts);
+    }
+
+    private static function _getClientId($params = null)
+    {
+        $clientId = ($params && array_key_exists('client_id', $params)) ? $params['client_id'] : null;
+        if ($clientId === null) {
+            $clientId = Stripe::getClientId();
+        }
+        if ($clientId === null) {
+            $msg = 'No client_id provided.  (HINT: set your client_id using '
+              . '"Stripe::setClientId(<CLIENT-ID>)".  You can find your client_ids '
+              . 'in your Stripe dashboard at '
+              . 'https://dashboard.stripe.com/account/applications/settings, '
+              . 'after registering your account as a platform. See '
+              . 'https://stripe.com/docs/connect/standard-accounts for details, '
+              . 'or email support@stripe.com if you have any questions.';
+            throw new Error\Authentication($msg);
+        }
+        return $clientId;
+    }
+}

+ 29 - 52
plugins/stripe/lib/Order.php

@@ -5,64 +5,41 @@ namespace Stripe;
 /**
  * Class Order
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_returned
+ * @property string $application
+ * @property int $application_fee
+ * @property string $charge
+ * @property int $created
+ * @property string $currency
+ * @property string $customer
+ * @property string $email
+ * @property string $external_coupon_code
+ * @property OrderItem[] $items
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property Collection $returns
+ * @property string $selected_shipping_method
+ * @property mixed $shipping
+ * @property mixed $shipping_methods
+ * @property string $status
+ * @property mixed $status_transitions
+ * @property int $updated
+ * @property string $upstream_id
+ *
  * @package Stripe
  */
 class Order extends ApiResource
 {
-    /**
-     * @param string $id The ID of the Order to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Order
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Order The created Order.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
+    const OBJECT_NAME = "order";
 
-    /**
-     * @param string $id The ID of the order to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Order The updated order.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Order The saved Order.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Orders
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
      * @return Order The paid order.

+ 22 - 0
plugins/stripe/lib/OrderItem.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class OrderItem
+ *
+ * @property string $object
+ * @property int $amount
+ * @property string $currency
+ * @property string $description
+ * @property string $parent
+ * @property int $quantity
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class OrderItem extends StripeObject
+{
+
+    const OBJECT_NAME = "order_item";
+}

+ 14 - 20
plugins/stripe/lib/OrderReturn.php

@@ -5,29 +5,23 @@ namespace Stripe;
 /**
  * Class OrderReturn
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $created
+ * @property string $currency
+ * @property OrderItem[] $items
+ * @property bool $livemode
+ * @property string $order
+ * @property string $refund
+ *
  * @package Stripe
  */
 class OrderReturn extends ApiResource
 {
-    /**
-     * @param string $id The ID of the OrderReturn to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Order
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of OrderReturns
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const OBJECT_NAME = "order_return";
+
+    use ApiOperations\All;
+    use ApiOperations\Retrieve;
 }

+ 90 - 0
plugins/stripe/lib/PaymentIntent.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class PaymentIntent
+ *
+ * @property string $id
+ * @property string $object
+ * @property string[] $allowed_source_types
+ * @property int $amount
+ * @property int $amount_capturable
+ * @property int $amount_received
+ * @property string $application
+ * @property int $application_fee
+ * @property int $canceled_at
+ * @property string $capture_method
+ * @property Collection $charges
+ * @property string $client_secret
+ * @property int $created
+ * @property string $currency
+ * @property string $customer
+ * @property string $description
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $next_source_action
+ * @property string $on_behalf_of
+ * @property string $receipt_email
+ * @property string $return_url
+ * @property mixed $shipping
+ * @property string $source
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property mixed $transfer_data
+ * @property string $transfer_group
+ *
+ * @package Stripe
+ */
+class PaymentIntent extends ApiResource
+{
+
+    const OBJECT_NAME = "payment_intent";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return PaymentIntent The canceled payment intent.
+     */
+    public function cancel($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return PaymentIntent The captured payment intent.
+     */
+    public function capture($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/capture';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return PaymentIntent The confirmed payment intent.
+     */
+    public function confirm($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/confirm';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}

+ 69 - 0
plugins/stripe/lib/Payout.php

@@ -0,0 +1,69 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Payout
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $arrival_date
+ * @property bool $automatic
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $destination
+ * @property string $failure_balance_transaction
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $method
+ * @property string $recipient
+ * @property string $source_type
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class Payout extends ApiResource
+{
+
+    const OBJECT_NAME = "payout";
+
+    /**
+     * Types of payout failure codes.
+     * @link https://stripe.com/docs/api#payout_failures
+     */
+    const FAILURE_ACCOUNT_CLOSED                = 'account_closed';
+    const FAILURE_ACCOUNT_FROZEN                = 'account_frozen';
+    const FAILURE_BANK_ACCOUNT_RESTRICTED       = 'bank_account_restricted';
+    const FAILURE_BANK_OWNERSHIP_CHANGED        = 'bank_ownership_changed';
+    const FAILURE_COULD_NOT_PROCESS             = 'could_not_process';
+    const FAILURE_DEBIT_NOT_AUTHORIZED          = 'debit_not_authorized';
+    const FAILURE_DECLINED                      = 'declined';
+    const FAILURE_INCORRECT_ACCOUNT_HOLDER_NAME = 'incorrect_account_holder_name';
+    const FAILURE_INSUFFICIENT_FUNDS            = 'insufficient_funds';
+    const FAILURE_INVALID_ACCOUNT_NUMBER        = 'invalid_account_number';
+    const FAILURE_INVALID_CURRENCY              = 'invalid_currency';
+    const FAILURE_NO_ACCOUNT                    = 'no_account';
+    const FAILURE_UNSUPPORTED_CARD              = 'unsupported_card';
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @return Payout The canceled payout.
+     */
+    public function cancel()
+    {
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}

+ 25 - 75
plugins/stripe/lib/Plan.php

@@ -7,84 +7,34 @@ namespace Stripe;
  *
  * @package Stripe
  *
- * @property $id
- * @property $object
- * @property $amount
- * @property $created
- * @property $currency
- * @property $interval
- * @property $interval_count
- * @property $livemode
- * @property AttachedObject $metadata
- * @property $name
- * @property $statement_descriptor
- * @property $trial_period_days
+ * @property string $id
+ * @property string $object
+ * @property bool $active
+ * @property string $aggregate_usage
+ * @property int $amount
+ * @property string $billing_scheme
+ * @property int $created
+ * @property string $currency
+ * @property string $interval
+ * @property int $interval_count
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $nickname
+ * @property string $product
+ * @property mixed $tiers
+ * @property string $tiers_mode
+ * @property mixed $transform_usage
+ * @property int $trial_period_days
+ * @property string $usage_type
  */
 class Plan extends ApiResource
 {
-    /**
-     * @param string $id The ID of the plan to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Plan
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Plan The created plan.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
+    const OBJECT_NAME = "plan";
 
-    /**
-     * @param string $id The ID of the plan to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Plan The updated plan.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Plan The deleted plan.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Plan The saved plan.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Plans
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 26 - 63
plugins/stripe/lib/Product.php

@@ -5,73 +5,36 @@ namespace Stripe;
 /**
  * Class Product
  *
+ * @property string $id
+ * @property string $object
+ * @property bool $active
+ * @property string[] $attributes
+ * @property string $caption
+ * @property int $created
+ * @property string[] $deactivate_on
+ * @property string $description
+ * @property array $images
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $name
+ * @property mixed $package_dimensions
+ * @property bool $shippable
+ * @property string $statement_descriptor
+ * @property string $type
+ * @property string $unit_label
+ * @property int $updated
+ * @property string $url
+ *
  * @package Stripe
  */
 class Product extends ApiResource
 {
-    /**
-     * @param string $id The ID of the Product to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Product
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Product The created Product.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the product to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Product The updated product.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Product The saved Product.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Products
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const OBJECT_NAME = "product";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Product The deleted product.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 22 - 66
plugins/stripe/lib/Recipient.php

@@ -6,74 +6,32 @@ namespace Stripe;
  * Class Recipient
  *
  * @package Stripe
+ *
+ * @property string $id
+ * @property string $object
+ * @property mixed $active_account
+ * @property Collection $cards
+ * @property int $created
+ * @property string $default_card
+ * @property string $description
+ * @property string $email
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $migrated_to
+ * @property string $name
+ * @property string $rolled_back_from
+ * @property string $type
  */
 class Recipient extends ApiResource
 {
-    /**
-     * @param string $id The ID of the recipient to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Recipient
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Recipients
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Recipient The created recipient.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
 
-    /**
-     * @param string $id The ID of the recipient to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Recipient The updated recipient.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Recipient The saved recipient.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
-    /**
-     * @param array|null $params
-     *
-     * @return Recipient The deleted recipient.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    const OBJECT_NAME = "recipient";
 
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
      * @param array|null $params
@@ -82,9 +40,7 @@ class Recipient extends ApiResource
      */
     public function transfers($params = null)
     {
-        if ($params === null) {
-            $params = array();
-        }
+        $params = $params ?: [];
         $params['recipient'] = $this->id;
         $transfers = Transfer::all($params, $this->_opts);
         return $transfers;

+ 39 - 0
plugins/stripe/lib/RecipientTransfer.php

@@ -0,0 +1,39 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class RecipientTransfer
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $amount_reversed
+ * @property string $balance_transaction
+ * @property string $bank_account
+ * @property string $card
+ * @property int $created
+ * @property string $currency
+ * @property int $date
+ * @property string $description
+ * @property string $destination
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property string $method
+ * @property string $recipient
+ * @property mixed $reversals
+ * @property bool $reversed
+ * @property string $source_type
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ *
+ * @package Stripe
+ */
+class RecipientTransfer extends ApiResource
+{
+
+    const OBJECT_NAME = "recipient_transfer";
+}

+ 9 - 55
plugins/stripe/lib/Refund.php

@@ -8,11 +8,13 @@ namespace Stripe;
  * @property string $id
  * @property string $object
  * @property int $amount
- * @property mixed $balance_transaction
+ * @property string $balance_transaction
  * @property string $charge
  * @property int $created
  * @property string $currency
- * @property mixed $metadata
+ * @property string $failure_balance_transaction
+ * @property string failure_reason
+ * @property StripeObject $metadata
  * @property mixed $reason
  * @property mixed $receipt_number
  * @property string $status
@@ -22,58 +24,10 @@ namespace Stripe;
 class Refund extends ApiResource
 {
 
-    /**
-     * @param string $id The ID of the refund to retrieve.
-     * @param array|string|null $options
-     *
-     * @return Refund
-     */
-    public static function retrieve($id, $options = null)
-    {
-        return self::_retrieve($id, $options);
-    }
+    const OBJECT_NAME = "refund";
 
-    /**
-     * @param string $id The ID of the refund to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Refund The updated refund.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Collection of Refunds
-     */
-    public static function all($params = null, $options = null)
-    {
-        return self::_all($params, $options);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Refund The created refund.
-     */
-    public static function create($params = null, $options = null)
-    {
-        return self::_create($params, $options);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Refund The saved refund.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 21 - 63
plugins/stripe/lib/SKU.php

@@ -5,73 +5,31 @@ namespace Stripe;
 /**
  * Class SKU
  *
+ * @property string $id
+ * @property string $object
+ * @property bool $active
+ * @property mixed $attributes
+ * @property int $created
+ * @property string $currency
+ * @property string $image
+ * @property mixed $inventory
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $package_dimensions
+ * @property int $price
+ * @property string $product
+ * @property int $updated
+ *
  * @package Stripe
  */
 class SKU extends ApiResource
 {
-    /**
-     * @param string $id The ID of the SKU to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return SKU
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return SKU The created SKU.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the SKU to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return SKU The updated SKU.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
-
-    /**
-     * @param array|string|null $opts
-     *
-     * @return SKU The saved SKU.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of SKUs
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const OBJECT_NAME = "sku";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return SKU The deleted sku.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 33 - 0
plugins/stripe/lib/Sigma/ScheduledQueryRun.php

@@ -0,0 +1,33 @@
+<?php
+
+namespace Stripe\Sigma;
+
+/**
+ * Class Authorization
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property int $data_load_time
+ * @property string $error
+ * @property \Stripe\FileUpload $file
+ * @property bool $livemode
+ * @property int $result_available_until
+ * @property string $sql
+ * @property string $status
+ * @property string $title
+ *
+ * @package Stripe\Sigma
+ */
+class ScheduledQueryRun extends \Stripe\ApiResource
+{
+    const OBJECT_NAME = "scheduled_query_run";
+
+    use \Stripe\ApiOperations\All;
+    use \Stripe\ApiOperations\Retrieve;
+
+    public static function classUrl()
+    {
+        return "/v1/sigma/scheduled_query_runs";
+    }
+}

+ 3 - 1
plugins/stripe/lib/SingletonApiResource.php

@@ -22,7 +22,9 @@ abstract class SingletonApiResource extends ApiResource
      */
     public static function classUrl()
     {
-        $base = static::className();
+        // Replace dots with slashes for namespaced resources, e.g. if the object's name is
+        // "foo.bar", then its URL will be "/v1/foo/bar".
+        $base = str_replace('.', '/', static::OBJECT_NAME);
         return "/v1/${base}";
     }
 

+ 65 - 33
plugins/stripe/lib/Source.php

@@ -5,70 +5,102 @@ namespace Stripe;
 /**
  * Class Source
  *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $client_secret
+ * @property mixed $code_verification
+ * @property int $created
+ * @property string $currency
+ * @property string $flow
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $owner
+ * @property mixed $receiver
+ * @property mixed $redirect
+ * @property string $statement_descriptor
+ * @property string $status
+ * @property string $type
+ * @property string $usage
+ *
  * @package Stripe
  */
 class Source extends ApiResource
 {
-    /**
-     * @param string $id The ID of the Source to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Source
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Sources
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const OBJECT_NAME = "source";
+
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 
     /**
      * @param array|null $params
-     * @param array|string|null $opts
+     * @param array|string|null $options
      *
-     * @return Source The created Source.
+     * @return Source The detached source.
      */
-    public static function create($params = null, $opts = null)
+    public function detach($params = null, $options = null)
     {
-        return self::_create($params, $opts);
+        self::_validateParams($params);
+
+        $id = $this['id'];
+        if (!$id) {
+            $class = get_class($this);
+            $msg = "Could not determine which URL to request: $class instance "
+             . "has invalid ID: $id";
+            throw new Error\InvalidRequest($msg, null);
+        }
+
+        if ($this['customer']) {
+            $base = Customer::classUrl();
+            $parentExtn = urlencode(Util\Util::utf8($this['customer']));
+            $extn = urlencode(Util\Util::utf8($id));
+            $url = "$base/$parentExtn/sources/$extn";
+
+            list($response, $opts) = $this->_request('delete', $url, $params, $options);
+            $this->refreshFrom($response, $opts);
+            return $this;
+        } else {
+            $message = "This source object does not appear to be currently attached "
+               . "to a customer object.";
+            throw new Error\Api($message);
+        }
     }
 
     /**
-     * @param string $id The ID of the source to update.
      * @param array|null $params
      * @param array|string|null $options
      *
-     * @return Source The updated source.
+     * @return Source The detached source.
+     *
+     * @deprecated Use the `detach` method instead.
      */
-    public static function update($id, $params = null, $options = null)
+    public function delete($params = null, $options = null)
     {
-        return self::_update($id, $params, $options);
+        $this->detach($params, $options);
     }
 
     /**
-     * @param array|string|null $opts
+     * @param array|null $params
+     * @param array|string|null $options
      *
-     * @return Source The saved source.
+     * @return Collection The list of source transactions.
      */
-    public function save($opts = null)
+    public function sourceTransactions($params = null, $options = null)
     {
-        return $this->_save($opts);
+        $url = $this->instanceUrl() . '/source_transactions';
+        list($response, $opts) = $this->_request('get', $url, $params, $options);
+        $obj = Util\Util::convertToStripeObject($response, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
     }
 
     /**
      * @param array|null $params
      * @param array|string|null $options
      *
-     * @return BankAccount The verified bank account.
+     * @return Source The verified source.
      */
     public function verify($params = null, $options = null)
     {

+ 23 - 0
plugins/stripe/lib/SourceTransaction.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class SourceTransaction
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property int $created
+ * @property string $customer_data
+ * @property string $currency
+ * @property string $type
+ * @property mixed $ach_credit_transfer
+ *
+ * @package Stripe
+ */
+class SourceTransaction extends ApiResource
+{
+
+    const OBJECT_NAME = "source_transaction";
+}

+ 122 - 6
plugins/stripe/lib/Stripe.php

@@ -12,9 +12,15 @@ class Stripe
     // @var string The Stripe API key to be used for requests.
     public static $apiKey;
 
+    // @var string The Stripe client_id to be used for Connect requests.
+    public static $clientId;
+
     // @var string The base URL for the Stripe API.
     public static $apiBase = 'https://api.stripe.com';
 
+    // @var string The base URL for the OAuth API.
+    public static $connectBase = 'https://connect.stripe.com';
+
     // @var string The base URL for the Stripe API uploads endpoint.
     public static $apiUploadBase = 'https://uploads.stripe.com';
 
@@ -24,13 +30,29 @@ class Stripe
     // @var string|null The account ID for connected accounts requests.
     public static $accountId = null;
 
+    // @var string Path to the CA bundle used to verify SSL certificates
+    public static $caBundlePath = null;
+
     // @var boolean Defaults to true.
     public static $verifySslCerts = true;
 
     // @var array The application's information (name, version, URL)
     public static $appInfo = null;
 
-    const VERSION = '4.4.0';
+    // @var Util\LoggerInterface|null The logger to which the library will
+    //   produce messages.
+    public static $logger = null;
+
+    // @var int Maximum number of request retries
+    public static $maxNetworkRetries = 0;
+
+    // @var float Maximum delay between retries, in seconds
+    private static $maxNetworkRetryDelay = 2.0;
+
+    // @var float Initial delay between retries, in seconds
+    private static $initialNetworkRetryDelay = 0.5;
+
+    const VERSION = '6.15.0';
 
     /**
      * @return string The API key used for requests.
@@ -40,6 +62,35 @@ class Stripe
         return self::$apiKey;
     }
 
+    /**
+     * @return string The client_id used for Connect requests.
+     */
+    public static function getClientId()
+    {
+        return self::$clientId;
+    }
+
+    /**
+     * @return Util\LoggerInterface The logger to which the library will
+     *   produce messages.
+     */
+    public static function getLogger()
+    {
+        if (self::$logger == null) {
+            return new Util\DefaultLogger();
+        }
+        return self::$logger;
+    }
+
+    /**
+     * @param Util\LoggerInterface $logger The logger to which the library
+     *   will produce messages.
+     */
+    public static function setLogger($logger)
+    {
+        self::$logger = $logger;
+    }
+
     /**
      * Sets the API key to be used for requests.
      *
@@ -50,6 +101,16 @@ class Stripe
         self::$apiKey = $apiKey;
     }
 
+    /**
+     * Sets the client_id to be used for Connect requests.
+     *
+     * @param string $clientId
+     */
+    public static function setClientId($clientId)
+    {
+        self::$clientId = $clientId;
+    }
+
     /**
      * @return string The API version used for requests. null if we're using the
      *    latest version.
@@ -67,6 +128,30 @@ class Stripe
         self::$apiVersion = $apiVersion;
     }
 
+    /**
+     * @return string
+     */
+    private static function getDefaultCABundlePath()
+    {
+        return realpath(dirname(__FILE__) . '/../data/ca-certificates.crt');
+    }
+
+    /**
+     * @return string
+     */
+    public static function getCABundlePath()
+    {
+        return self::$caBundlePath ?: self::getDefaultCABundlePath();
+    }
+
+    /**
+     * @param string $caBundlePath
+     */
+    public static function setCABundlePath($caBundlePath)
+    {
+        self::$caBundlePath = $caBundlePath;
+    }
+
     /**
      * @return boolean
      */
@@ -114,13 +199,44 @@ class Stripe
      * @param string $appVersion The application's version
      * @param string $appUrl The application's URL
      */
-    public static function setAppInfo($appName, $appVersion = null, $appUrl = null)
+    public static function setAppInfo($appName, $appVersion = null, $appUrl = null, $appPartnerId = null)
     {
-        if (self::$appInfo === null) {
-            self::$appInfo = array();
-        }
+        self::$appInfo = self::$appInfo ?: [];
         self::$appInfo['name'] = $appName;
-        self::$appInfo['version'] = $appVersion;
+        self::$appInfo['partner_id'] = $appPartnerId;
         self::$appInfo['url'] = $appUrl;
+        self::$appInfo['version'] = $appVersion;
+    }
+
+    /**
+     * @return int Maximum number of request retries
+     */
+    public static function getMaxNetworkRetries()
+    {
+        return self::$maxNetworkRetries;
+    }
+
+    /**
+     * @param int $maxNetworkRetries Maximum number of request retries
+     */
+    public static function setMaxNetworkRetries($maxNetworkRetries)
+    {
+        self::$maxNetworkRetries = $maxNetworkRetries;
+    }
+
+    /**
+     * @return float Maximum delay between retries, in seconds
+     */
+    public static function getMaxNetworkRetryDelay()
+    {
+        return self::$maxNetworkRetryDelay;
+    }
+
+    /**
+     * @return float Initial delay between retries, in seconds
+     */
+    public static function getInitialNetworkRetryDelay()
+    {
+        return self::$initialNetworkRetryDelay;
     }
 }

+ 344 - 115
plugins/stripe/lib/StripeObject.php

@@ -2,119 +2,151 @@
 
 namespace Stripe;
 
-use ArrayAccess;
-use InvalidArgumentException;
-
 /**
  * Class StripeObject
  *
  * @package Stripe
  */
-class StripeObject implements ArrayAccess, JsonSerializable
+class StripeObject implements \ArrayAccess, \Countable, \JsonSerializable
 {
-    /**
-     * @var Util\Set Attributes that should not be sent to the API because
-     *    they're not updatable (e.g. API key, ID).
-     */
-    public static $permanentAttributes;
-    /**
-     * @var Util\Set Attributes that are nested but still updatable from
-     *    the parent class's URL (e.g. metadata).
-     */
-    public static $nestedUpdatableAttributes;
-
-    public static function init()
-    {
-        self::$permanentAttributes = new Util\Set(array('_opts', 'id'));
-        self::$nestedUpdatableAttributes = new Util\Set(array(
-            'metadata', 'legal_entity', 'address', 'dob', 'transfer_schedule', 'verification',
-            'tos_acceptance', 'personal_address',
-            // will make the array into an AttachedObject: weird, but works for now
-            'additional_owners', 0, 1, 2, 3, 4, // Max 3, but leave the 4th so errors work properly
-            'inventory'
-        ));
-    }
+    protected $_opts;
+    protected $_originalValues;
+    protected $_values;
+    protected $_unsavedValues;
+    protected $_transientValues;
+    protected $_retrieveOptions;
+    protected $_lastResponse;
 
     /**
-     * @return object The last response from the Stripe API
+     * @return Util\Set Attributes that should not be sent to the API because
+     *    they're not updatable (e.g. ID).
      */
-    public function getLastResponse()
+    public static function getPermanentAttributes()
     {
-        return $this->_lastResponse;
+        static $permanentAttributes = null;
+        if ($permanentAttributes === null) {
+            $permanentAttributes = new Util\Set([
+                'id',
+            ]);
+        }
+        return $permanentAttributes;
     }
 
     /**
-     * @param ApiResponse
+     * Additive objects are subobjects in the API that don't have the same
+     * semantics as most subobjects, which are fully replaced when they're set.
+     * This is best illustrated by example. The `source` parameter sent when
+     * updating a subscription is *not* additive; if we set it:
      *
-     * @return void Set the last response from the Stripe API
+     *     source[object]=card&source[number]=123
+     *
+     * We expect the old `source` object to have been overwritten completely. If
+     * the previous source had an `address_state` key associated with it and we
+     * didn't send one this time, that value of `address_state` is gone.
+     *
+     * By contrast, additive objects are those that will have new data added to
+     * them while keeping any existing data in place. The only known case of its
+     * use is for `metadata`, but it could in theory be more general. As an
+     * example, say we have a `metadata` object that looks like this on the
+     * server side:
+     *
+     *     metadata = ["old" => "old_value"]
+     *
+     * If we update the object with `metadata[new]=new_value`, the server side
+     * object now has *both* fields:
+     *
+     *     metadata = ["old" => "old_value", "new" => "new_value"]
+     *
+     * This is okay in itself because usually users will want to treat it as
+     * additive:
+     *
+     *     $obj->metadata["new"] = "new_value";
+     *     $obj->save();
+     *
+     * However, in other cases, they may want to replace the entire existing
+     * contents:
+     *
+     *     $obj->metadata = ["new" => "new_value"];
+     *     $obj->save();
+     *
+     * This is where things get a little bit tricky because in order to clear
+     * any old keys that may have existed, we actually have to send an explicit
+     * empty string to the server. So the operation above would have to send
+     * this form to get the intended behavior:
+     *
+     *     metadata[old]=&metadata[new]=new_value
+     *
+     * This method allows us to track which parameters are considered additive,
+     * and lets us behave correctly where appropriate when serializing
+     * parameters to be sent.
+     *
+     * @return Util\Set Set of additive parameters
      */
-    public function setLastResponse($resp)
+    public static function getAdditiveParams()
     {
-        $this->_lastResponse = $resp;
+        static $additiveParams = null;
+        if ($additiveParams === null) {
+            // Set `metadata` as additive so that when it's set directly we remember
+            // to clear keys that may have been previously set by sending empty
+            // values for them.
+            //
+            // It's possible that not every object has `metadata`, but having this
+            // option set when there is no `metadata` field is not harmful.
+            $additiveParams = new Util\Set([
+                'metadata',
+            ]);
+        }
+        return $additiveParams;
     }
 
-    protected $_opts;
-    protected $_values;
-    protected $_unsavedValues;
-    protected $_transientValues;
-    protected $_retrieveOptions;
-    protected $_lastResponse;
-
     public function __construct($id = null, $opts = null)
     {
-        $this->_opts = $opts ? $opts : new Util\RequestOptions();
-        $this->_values = array();
+        list($id, $this->_retrieveOptions) = Util\Util::normalizeId($id);
+        $this->_opts = Util\RequestOptions::parse($opts);
+        $this->_originalValues = [];
+        $this->_values = [];
         $this->_unsavedValues = new Util\Set();
         $this->_transientValues = new Util\Set();
-
-        $this->_retrieveOptions = array();
-        if (is_array($id)) {
-            foreach ($id as $key => $value) {
-                if ($key != 'id') {
-                    $this->_retrieveOptions[$key] = $value;
-                }
-            }
-            $id = $id['id'];
-        }
-
         if ($id !== null) {
-            $this->id = $id;
+            $this->_values['id'] = $id;
         }
     }
 
     // Standard accessor magic methods
     public function __set($k, $v)
     {
+        if (static::getPermanentAttributes()->includes($k)) {
+            throw new \InvalidArgumentException(
+                "Cannot set $k on this object. HINT: you can't set: " .
+                join(', ', static::getPermanentAttributes()->toArray())
+            );
+        }
+
         if ($v === "") {
-            throw new InvalidArgumentException(
+            throw new \InvalidArgumentException(
                 'You cannot set \''.$k.'\'to an empty string. '
                 .'We interpret empty strings as NULL in requests. '
                 .'You may set obj->'.$k.' = NULL to delete the property'
             );
         }
 
-        if (self::$nestedUpdatableAttributes->includes($k)
-                && isset($this->$k) && $this->$k instanceof AttachedObject && is_array($v)) {
-            $this->$k->replaceWith($v);
-        } else {
-            // TODO: may want to clear from $_transientValues (Won't be user-visible).
-            $this->_values[$k] = $v;
-        }
-        if (!self::$permanentAttributes->includes($k)) {
-            $this->_unsavedValues->add($k);
-        }
+        $this->_values[$k] = Util\Util::convertToStripeObject($v, $this->_opts);
+        $this->dirtyValue($this->_values[$k]);
+        $this->_unsavedValues->add($k);
     }
 
     public function __isset($k)
     {
         return isset($this->_values[$k]);
     }
+
     public function __unset($k)
     {
         unset($this->_values[$k]);
         $this->_transientValues->add($k);
         $this->_unsavedValues->discard($k);
     }
+
     public function &__get($k)
     {
         // function should return a reference, using $nullval to return a reference to null
@@ -130,15 +162,21 @@ class StripeObject implements ArrayAccess, JsonSerializable
                     . "with the result returned by Stripe's API, "
                     . "probably as a result of a save(). The attributes currently "
                     . "available on this object are: $attrs";
-            error_log($message);
+            Stripe::getLogger()->error($message);
             return $nullval;
         } else {
             $class = get_class($this);
-            error_log("Stripe Notice: Undefined property of $class instance: $k");
+            Stripe::getLogger()->error("Stripe Notice: Undefined property of $class instance: $k");
             return $nullval;
         }
     }
 
+    // Magic method for var_dump output. Only works with PHP >= 5.6
+    public function __debugInfo()
+    {
+        return $this->_values;
+    }
+
     // ArrayAccess methods
     public function offsetSet($k, $v)
     {
@@ -154,25 +192,37 @@ class StripeObject implements ArrayAccess, JsonSerializable
     {
         unset($this->$k);
     }
+
     public function offsetGet($k)
     {
         return array_key_exists($k, $this->_values) ? $this->_values[$k] : null;
     }
 
+    // Countable method
+    public function count()
+    {
+        return count($this->_values);
+    }
+
     public function keys()
     {
         return array_keys($this->_values);
     }
 
+    public function values()
+    {
+        return array_values($this->_values);
+    }
+
     /**
      * This unfortunately needs to be public to be used in Util\Util
      *
      * @param array $values
-     * @param array $opts
+     * @param null|string|array|Util\RequestOptions $opts
      *
      * @return StripeObject The object constructed from the given values.
      */
-    public static function constructFrom($values, $opts)
+    public static function constructFrom($values, $opts = null)
     {
         $obj = new static(isset($values['id']) ? $values['id'] : null);
         $obj->refreshFrom($values, $opts);
@@ -183,16 +233,18 @@ class StripeObject implements ArrayAccess, JsonSerializable
      * Refreshes this object using the provided values.
      *
      * @param array $values
-     * @param array|Util\RequestOptions $opts
+     * @param null|string|array|Util\RequestOptions $opts
      * @param boolean $partial Defaults to false.
      */
     public function refreshFrom($values, $opts, $partial = false)
     {
-        if (is_array($opts)) {
-            $opts = Util\RequestOptions::parse($opts);
-        }
+        $this->_opts = Util\RequestOptions::parse($opts);
 
-        $this->_opts = $opts;
+        $this->_originalValues = self::deepCopy($values);
+
+        if ($values instanceof StripeObject) {
+            $values = $values->__toArray(true);
+        }
 
         // Wipe old state before setting new.  This is useful for e.g. updating a
         // customer, where there is no persistent card parameter.  Mark those values
@@ -200,30 +252,43 @@ class StripeObject implements ArrayAccess, JsonSerializable
         if ($partial) {
             $removed = new Util\Set();
         } else {
-            $removed = array_diff(array_keys($this->_values), array_keys($values));
+            $removed = new Util\Set(array_diff(array_keys($this->_values), array_keys($values)));
         }
 
-        foreach ($removed as $k) {
-            if (self::$permanentAttributes->includes($k)) {
-                continue;
-            }
-
+        foreach ($removed->toArray() as $k) {
             unset($this->$k);
         }
 
+        $this->updateAttributes($values, $opts, false);
         foreach ($values as $k => $v) {
-            if (self::$permanentAttributes->includes($k) && isset($this[$k])) {
-                continue;
-            }
+            $this->_transientValues->discard($k);
+            $this->_unsavedValues->discard($k);
+        }
+    }
 
-            if (self::$nestedUpdatableAttributes->includes($k) && is_array($v)) {
-                $this->_values[$k] = AttachedObject::constructFrom($v, $opts);
+    /**
+     * Mass assigns attributes on the model.
+     *
+     * @param array $values
+     * @param null|string|array|Util\RequestOptions $opts
+     * @param boolean $dirty Defaults to true.
+     */
+    public function updateAttributes($values, $opts = null, $dirty = true)
+    {
+        foreach ($values as $k => $v) {
+            // Special-case metadata to always be cast as a StripeObject
+            // This is necessary in case metadata is empty, as PHP arrays do
+            // not differentiate between lists and hashes, and we consider
+            // empty arrays to be lists.
+            if ($k === "metadata") {
+                $this->_values[$k] = StripeObject::constructFrom($v, $opts);
             } else {
                 $this->_values[$k] = Util\Util::convertToStripeObject($v, $opts);
             }
-
-            $this->_transientValues->discard($k);
-            $this->_unsavedValues->discard($k);
+            if ($dirty) {
+                $this->dirtyValue($this->_values[$k]);
+            }
+            $this->_unsavedValues->add($k);
         }
     }
 
@@ -231,33 +296,107 @@ class StripeObject implements ArrayAccess, JsonSerializable
      * @return array A recursive mapping of attributes to values for this object,
      *    including the proper value for deleted attributes.
      */
-    public function serializeParameters()
+    public function serializeParameters($force = false)
     {
-        $params = array();
-        if ($this->_unsavedValues) {
-            foreach ($this->_unsavedValues->toArray() as $k) {
-                $v = $this->$k;
-                if ($v === null) {
-                    $v = '';
-                }
-
-                $params[$k] = $v;
+        $updateParams = [];
+
+        foreach ($this->_values as $k => $v) {
+            // There are a few reasons that we may want to add in a parameter for
+            // update:
+            //
+            //   1. The `$force` option has been set.
+            //   2. We know that it was modified.
+            //   3. Its value is a StripeObject. A StripeObject may contain modified
+            //      values within in that its parent StripeObject doesn't know about.
+            //
+            $original = array_key_exists($k, $this->_originalValues) ? $this->_originalValues[$k] : null;
+            $unsaved = $this->_unsavedValues->includes($k);
+            if ($force || $unsaved || $v instanceof StripeObject) {
+                $updateParams[$k] = $this->serializeParamsValue(
+                    $this->_values[$k],
+                    $original,
+                    $unsaved,
+                    $force,
+                    $k
+                );
             }
         }
 
-        // Get nested updates.
-        foreach (self::$nestedUpdatableAttributes->toArray() as $property) {
-            if (isset($this->$property)) {
-                if ($this->$property instanceof StripeObject) {
-                    $serialized = $this->$property->serializeParameters();
-                    if ($serialized) {
-                        $params[$property] = $serialized;
-                    }
+        // a `null` that makes it out of `serializeParamsValue` signals an empty
+        // value that we shouldn't appear in the serialized form of the object
+        $updateParams = array_filter(
+            $updateParams,
+            function ($v) {
+                return $v !== null;
+            }
+        );
+
+        return $updateParams;
+    }
+
+
+    public function serializeParamsValue($value, $original, $unsaved, $force, $key = null)
+    {
+        // The logic here is that essentially any object embedded in another
+        // object that had a `type` is actually an API resource of a different
+        // type that's been included in the response. These other resources must
+        // be updated from their proper endpoints, and therefore they are not
+        // included when serializing even if they've been modified.
+        //
+        // There are _some_ known exceptions though.
+        //
+        // For example, if the value is unsaved (meaning the user has set it), and
+        // it looks like the API resource is persisted with an ID, then we include
+        // the object so that parameters are serialized with a reference to its
+        // ID.
+        //
+        // Another example is that on save API calls it's sometimes desirable to
+        // update a customer's default source by setting a new card (or other)
+        // object with `->source=` and then saving the customer. The
+        // `saveWithParent` flag to override the default behavior allows us to
+        // handle these exceptions.
+        //
+        // We throw an error if a property was set explicitly but we can't do
+        // anything with it because the integration is probably not working as the
+        // user intended it to.
+        if ($value === null) {
+            return "";
+        } elseif (($value instanceof APIResource) && (!$value->saveWithParent)) {
+            if (!$unsaved) {
+                return null;
+            } elseif (isset($value->id)) {
+                return $value;
+            } else {
+                throw new \InvalidArgumentException(
+                    "Cannot save property `$key` containing an API resource of type " .
+                    get_class($value) . ". It doesn't appear to be persisted and is " .
+                    "not marked as `saveWithParent`."
+                );
+            }
+        } elseif (is_array($value)) {
+            if (Util\Util::isList($value)) {
+                // Sequential array, i.e. a list
+                $update = [];
+                foreach ($value as $v) {
+                    array_push($update, $this->serializeParamsValue($v, null, true, $force));
+                }
+                // This prevents an array that's unchanged from being resent.
+                if ($update !== $this->serializeParamsValue($original, null, true, $force, $key)) {
+                    return $update;
                 }
+            } else {
+                // Associative array, i.e. a map
+                return Util\Util::convertToStripeObject($value, $this->_opts)->serializeParameters();
+            }
+        } elseif ($value instanceof StripeObject) {
+            $update = $value->serializeParameters($force);
+            if ($original && $unsaved && $key && static::getAdditiveParams()->includes($key)) {
+                $update = array_merge(self::emptyValues($original), $update);
             }
+            return $update;
+        } else {
+            return $value;
         }
-
-        return $params;
     }
 
     public function jsonSerialize()
@@ -267,11 +406,7 @@ class StripeObject implements ArrayAccess, JsonSerializable
 
     public function __toJSON()
     {
-        if (defined('JSON_PRETTY_PRINT')) {
-            return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
-        } else {
-            return json_encode($this->__toArray(true));
-        }
+        return json_encode($this->__toArray(true), JSON_PRETTY_PRINT);
     }
 
     public function __toString()
@@ -288,6 +423,100 @@ class StripeObject implements ArrayAccess, JsonSerializable
             return $this->_values;
         }
     }
-}
 
-StripeObject::init();
+    /**
+     * Sets all keys within the StripeObject as unsaved so that they will be
+     * included with an update when `serializeParameters` is called. This
+     * method is also recursive, so any StripeObjects contained as values or
+     * which are values in a tenant array are also marked as dirty.
+     */
+    public function dirty()
+    {
+        $this->_unsavedValues = new Util\Set(array_keys($this->_values));
+        foreach ($this->_values as $k => $v) {
+            $this->dirtyValue($v);
+        }
+    }
+
+    protected function dirtyValue($value)
+    {
+        if (is_array($value)) {
+            foreach ($value as $v) {
+                $this->dirtyValue($v);
+            }
+        } elseif ($value instanceof StripeObject) {
+            $value->dirty();
+        }
+    }
+
+    /**
+     * Produces a deep copy of the given object including support for arrays
+     * and StripeObjects.
+     */
+    protected static function deepCopy($obj)
+    {
+        if (is_array($obj)) {
+            $copy = [];
+            foreach ($obj as $k => $v) {
+                $copy[$k] = self::deepCopy($v);
+            }
+            return $copy;
+        } elseif ($obj instanceof StripeObject) {
+            return $obj::constructFrom(
+                self::deepCopy($obj->_values),
+                clone $obj->_opts
+            );
+        } else {
+            return $obj;
+        }
+    }
+
+    /**
+     * Returns a hash of empty values for all the values that are in the given
+     * StripeObject.
+     */
+    public static function emptyValues($obj)
+    {
+        if (is_array($obj)) {
+            $values = $obj;
+        } elseif ($obj instanceof StripeObject) {
+            $values = $obj->_values;
+        } else {
+            throw new \InvalidArgumentException(
+                "empty_values got got unexpected object type: " . get_class($obj)
+            );
+        }
+        $update = array_fill_keys(array_keys($values), "");
+        return $update;
+    }
+
+    /**
+     * @return object The last response from the Stripe API
+     */
+    public function getLastResponse()
+    {
+        return $this->_lastResponse;
+    }
+
+    /**
+     * @param ApiResponse
+     *
+     * @return void Set the last response from the Stripe API
+     */
+    public function setLastResponse($resp)
+    {
+        $this->_lastResponse = $resp;
+    }
+
+    /**
+     * Indicates whether or not the resource has been deleted on the server.
+     * Note that some, but not all, resources can indicate whether they have
+     * been deleted.
+     *
+     * @return bool Whether the resource is deleted.
+     */
+    public function isDeleted()
+    {
+        return isset($this->_values['deleted']) ? $this->_values['deleted'] : false;
+    }
+}

+ 47 - 55
plugins/stripe/lib/Subscription.php

@@ -5,64 +5,66 @@ namespace Stripe;
 /**
  * Class Subscription
  *
+ * @property string $id
+ * @property string $object
+ * @property float $application_fee_percent
+ * @property string $billing
+ * @property int $billing_cycle_anchor
+ * @property bool $cancel_at_period_end
+ * @property int $canceled_at
+ * @property int $created
+ * @property int current_period_end
+ * @property int current_period_start
+ * @property string $customer
+ * @property int $days_until_due
+ * @property Discount $discount
+ * @property int $ended_at
+ * @property Collection $items
+ * @property boolean $livemode
+ * @property StripeObject $metadata
+ * @property Plan $plan
+ * @property int $quantity
+ * @property int $start
+ * @property string $status
+ * @property float $tax_percent
+ * @property int $trial_end
+ * @property int $trial_start
+ *
  * @package Stripe
  */
 class Subscription extends ApiResource
 {
+
+    const OBJECT_NAME = "subscription";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete {
+        delete as protected _delete;
+    }
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
     /**
      * These constants are possible representations of the status field.
      *
      * @link https://stripe.com/docs/api#subscription_object-status
      */
-    const STATUS_ACTIVE = 'active';
+    const STATUS_ACTIVE   = 'active';
     const STATUS_CANCELED = 'canceled';
     const STATUS_PAST_DUE = 'past_due';
     const STATUS_TRIALING = 'trialing';
-    const STATUS_UNPAID = 'unpaid';
-
-    /**
-     * @param string $id The ID of the subscription to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Subscription
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Subscriptions
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
+    const STATUS_UNPAID   = 'unpaid';
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Subscription The created subscription.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the subscription to retrieve.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return Subscription The updated subscription.
-     */
-    public static function update($id, $params = null, $options = null)
+    public static function getSavedNestedResources()
     {
-        return self::_update($id, $params, $options);
+        static $savedNestedResources = null;
+        if ($savedNestedResources === null) {
+            $savedNestedResources = new Util\Set([
+                'source',
+            ]);
+        }
+        return $savedNestedResources;
     }
 
     /**
@@ -75,16 +77,6 @@ class Subscription extends ApiResource
         return $this->_delete($params, $opts);
     }
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return Subscription The saved subscription.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
-
     /**
      * @return Subscription The updated subscription.
      */
@@ -92,6 +84,6 @@ class Subscription extends ApiResource
     {
         $url = $this->instanceUrl() . '/discount';
         list($response, $opts) = $this->_request('delete', $url);
-        $this->refreshFrom(array('discount' => null), $opts, true);
+        $this->refreshFrom(['discount' => null], $opts, true);
     }
 }

+ 14 - 74
plugins/stripe/lib/SubscriptionItem.php

@@ -5,84 +5,24 @@ namespace Stripe;
 /**
  * Class SubscriptionItem
  *
+ * @property string $id
+ * @property string $object
+ * @property int $created
+ * @property StripeObject $metadata
+ * @property Plan $plan
+ * @property int $quantity
+ * @property string $subscription
+ *
  * @package Stripe
  */
 class SubscriptionItem extends ApiResource
 {
-    /**
-     * This is a special case because the subscription items endpoint has an
-     *    underscore in it. The parent `className` function strips underscores.
-     *
-     * @return string The name of the class.
-     */
-    public static function className()
-    {
-        return 'subscription_item';
-    }
-
-    /**
-     * @param string $id The ID of the subscription item to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return SubscriptionItem
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of SubscriptionItems
-     */
-    public static function all($params = null, $opts = null)
-    {
-        return self::_all($params, $opts);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return SubscriptionItem The created subscription item.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
-
-    /**
-     * @param string $id The ID of the subscription item to update.
-     * @param array|null $params
-     * @param array|string|null $options
-     *
-     * @return SubscriptionItem The updated subscription item.
-     */
-    public static function update($id, $params = null, $options = null)
-    {
-        return self::_update($id, $params, $options);
-    }
 
-    /**
-     * @param array|string|null $opts
-     *
-     * @return SubscriptionItem The saved subscription item.
-     */
-    public function save($opts = null)
-    {
-        return $this->_save($opts);
-    }
+    const OBJECT_NAME = "subscription_item";
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return SubscriptionItem The deleted subscription item.
-     */
-    public function delete($params = null, $opts = null)
-    {
-        return $this->_delete($params, $opts);
-    }
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Delete;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
 }

+ 6 - 22
plugins/stripe/lib/ThreeDSecure.php

@@ -4,6 +4,12 @@ namespace Stripe;
 
 class ThreeDSecure extends ApiResource
 {
+
+    const OBJECT_NAME = "three_d_secure";
+
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+
     /**
      * @return string The endpoint URL for the given class.
      */
@@ -11,26 +17,4 @@ class ThreeDSecure extends ApiResource
     {
         return "/v1/3d_secure";
     }
-
-    /**
-     * @param string $id The ID of the 3DS auth to retrieve.
-     * @param array|string|null $options
-     *
-     * @return ThreeDSecure
-     */
-    public static function retrieve($id, $options = null)
-    {
-        return self::_retrieve($id, $options);
-    }
-
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return ThreeDSecure The created 3D Secure object.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
 }

+ 7 - 23
plugins/stripe/lib/Token.php

@@ -7,9 +7,9 @@ namespace Stripe;
  *
  * @property string $id
  * @property string $object
- * @property mixed $bank_account
- * @property mixed $card
- * @property mixed $client_ip
+ * @property BankAccount $bank_account
+ * @property Card $card
+ * @property string $client_ip
  * @property int $created
  * @property bool $livemode
  * @property string $type
@@ -19,25 +19,9 @@ namespace Stripe;
  */
 class Token extends ApiResource
 {
-    /**
-     * @param string $id The ID of the token to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Token
-     */
-    public static function retrieve($id, $opts = null)
-    {
-        return self::_retrieve($id, $opts);
-    }
 
-    /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Token The created token.
-     */
-    public static function create($params = null, $opts = null)
-    {
-        return self::_create($params, $opts);
-    }
+    const OBJECT_NAME = "token";
+
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
 }

+ 49 - 0
plugins/stripe/lib/Topup.php

@@ -0,0 +1,49 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class Topup
+ *
+ * @property string $id
+ * @property string $object
+ * @property int $amount
+ * @property string $balance_transaction
+ * @property int $created
+ * @property string $currency
+ * @property string $description
+ * @property int $expected_availability_date
+ * @property string $failure_code
+ * @property string $failure_message
+ * @property bool $livemode
+ * @property StripeObject $metadata
+ * @property mixed $source
+ * @property string $statement_descriptor
+ * @property string $status
+ *
+ * @package Stripe
+ */
+class Topup extends ApiResource
+{
+
+    const OBJECT_NAME = "topup";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return Topup The canceled topup.
+     */
+    public function cancel($params = null, $options = null)
+    {
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url, $params, $options);
+        $this->refreshFrom($response, $opts);
+        return $this;
+    }
+}

+ 53 - 56
plugins/stripe/lib/Transfer.php

@@ -9,107 +9,104 @@ namespace Stripe;
  * @property string $object
  * @property int $amount
  * @property int $amount_reversed
- * @property mixed $application_fee
  * @property string $balance_transaction
  * @property int $created
  * @property string $currency
- * @property int $date
- * @property mixed $description
- * @property mixed $destination
- * @property mixed $destination_payment
- * @property mixed $failure_code
- * @property mixed $failure_message
- * @property mixed $fraud_details
- * @property mixed $invoice
+ * @property string $description
+ * @property string $destination
+ * @property string $destination_payment
  * @property bool $livemode
- * @property mixed $metadata
- * @property mixed $recipient
- * @property mixed $reversals
+ * @property StripeObject $metadata
+ * @property Collection $reversals
  * @property bool $reversed
- * @property mixed $source_transaction
+ * @property string $source_transaction
  * @property string $source_type
- * @property mixed $statement_descriptor
- * @property string $status
- * @property string $type
+ * @property string $transfer_group
  *
  * @package Stripe
  */
 class Transfer extends ApiResource
 {
+
+    const OBJECT_NAME = "transfer";
+
+    use ApiOperations\All;
+    use ApiOperations\Create;
+    use ApiOperations\NestedResource;
+    use ApiOperations\Retrieve;
+    use ApiOperations\Update;
+
+    const PATH_REVERSALS = '/reversals';
+
     /**
-     * @param string $id The ID of the transfer to retrieve.
-     * @param array|string|null $opts
-     *
-     * @return Transfer
+     * @return TransferReversal The created transfer reversal.
      */
-    public static function retrieve($id, $opts = null)
+    public function reverse($params = null, $opts = null)
     {
-        return self::_retrieve($id, $opts);
+        $url = $this->instanceUrl() . '/reversals';
+        list($response, $opts) = $this->_request('post', $url, $params, $opts);
+        $this->refreshFrom($response, $opts);
+        return $this;
     }
 
     /**
-     * @param array|null $params
-     * @param array|string|null $opts
-     *
-     * @return Collection of Transfers
+     * @return Transfer The canceled transfer.
      */
-    public static function all($params = null, $opts = null)
+    public function cancel()
     {
-        return self::_all($params, $opts);
+        $url = $this->instanceUrl() . '/cancel';
+        list($response, $opts) = $this->_request('post', $url);
+        $this->refreshFrom($response, $opts);
+        return $this;
     }
 
     /**
+     * @param array|null $id The ID of the transfer on which to create the reversal.
      * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Transfer The created transfer.
+     * @return TransferReversal
      */
-    public static function create($params = null, $opts = null)
+    public static function createReversal($id, $params = null, $opts = null)
     {
-        return self::_create($params, $opts);
+        return self::_createNestedResource($id, static::PATH_REVERSALS, $params, $opts);
     }
 
     /**
-     * @param string $id The ID of the transfer to update.
+     * @param array|null $id The ID of the transfer to which the reversal belongs.
+     * @param array|null $reversalId The ID of the reversal to retrieve.
      * @param array|null $params
-     * @param array|string|null $options
+     * @param array|string|null $opts
      *
-     * @return Transfer The updated transfer.
+     * @return TransferReversal
      */
-    public static function update($id, $params = null, $options = null)
+    public static function retrieveReversal($id, $reversalId, $params = null, $opts = null)
     {
-        return self::_update($id, $params, $options);
+        return self::_retrieveNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts);
     }
 
     /**
-     * @return TransferReversal The created transfer reversal.
-     */
-    public function reverse($params = null, $opts = null)
-    {
-        $url = $this->instanceUrl() . '/reversals';
-        list($response, $opts) = $this->_request('post', $url, $params, $opts);
-        $this->refreshFrom($response, $opts);
-        return $this;
-    }
-
-    /**
-     * @return Transfer The canceled transfer.
+     * @param array|null $id The ID of the transfer to which the reversal belongs.
+     * @param array|null $reversalId The ID of the reversal to update.
+     * @param array|null $params
+     * @param array|string|null $opts
+     *
+     * @return TransferReversal
      */
-    public function cancel()
+    public static function updateReversal($id, $reversalId, $params = null, $opts = null)
     {
-        $url = $this->instanceUrl() . '/cancel';
-        list($response, $opts) = $this->_request('post', $url);
-        $this->refreshFrom($response, $opts);
-        return $this;
+        return self::_updateNestedResource($id, static::PATH_REVERSALS, $reversalId, $params, $opts);
     }
 
     /**
+     * @param array|null $id The ID of the transfer on which to retrieve the reversals.
+     * @param array|null $params
      * @param array|string|null $opts
      *
-     * @return Transfer The saved transfer.
+     * @return TransferReversal
      */
-    public function save($opts = null)
+    public static function allReversals($id, $params = null, $opts = null)
     {
-        return $this->_save($opts);
+        return self::_allNestedResources($id, static::PATH_REVERSALS, $params, $opts);
     }
 }

+ 8 - 1
plugins/stripe/lib/TransferReversal.php

@@ -11,13 +11,20 @@ namespace Stripe;
  * @property string $balance_transaction
  * @property int $created
  * @property string $currency
- * @property mixed $metadata
+ * @property StripeObject $metadata
  * @property string $transfer
  *
  * @package Stripe
  */
 class TransferReversal extends ApiResource
 {
+
+    const OBJECT_NAME = "transfer_reversal";
+
+    use ApiOperations\Update {
+        save as protected _save;
+    }
+
     /**
      * @return string The API URL for this Stripe transfer reversal.
      */

+ 44 - 0
plugins/stripe/lib/UsageRecord.php

@@ -0,0 +1,44 @@
+<?php
+
+namespace Stripe;
+
+/**
+ * Class UsageRecord
+ *
+ * @package Stripe
+ *
+ * @property string $id
+ * @property string $object
+ * @property bool $livemode
+ * @property int $quantity
+ * @property string $subscription_item
+ * @property int $timestamp
+ */
+class UsageRecord extends ApiResource
+{
+
+    const OBJECT_NAME = "usage_record";
+
+    /**
+     * @param array|null $params
+     * @param array|string|null $options
+     *
+     * @return \Stripe\ApiResource The created resource.
+     */
+    public static function create($params = null, $options = null)
+    {
+        self::_validateParams($params);
+        if (!array_key_exists('subscription_item', $params)) {
+            throw new Error\InvalidRequest("Missing subscription_item param in request", null);
+        }
+        $subscription_item = $params['subscription_item'];
+        $url = "/v1/subscription_items/$subscription_item/usage_records";
+        $request_params = $params;
+        unset($request_params['subscription_item']);
+
+        list($response, $opts) = static::_staticRequest('post', $url, $request_params, $options);
+        $obj = \Stripe\Util\Util::convertToStripeObject($response->json, $opts);
+        $obj->setLastResponse($response);
+        return $obj;
+    }
+}

+ 3 - 3
plugins/stripe/lib/Util/AutoPagingIterator.php

@@ -7,7 +7,7 @@ class AutoPagingIterator implements \Iterator
     private $lastId = null;
     private $page = null;
     private $pageOffset = 0;
-    private $params = array();
+    private $params = [];
 
     public function __construct($collection, $params)
     {
@@ -42,8 +42,8 @@ class AutoPagingIterator implements \Iterator
             $this->pageOffset += count($this->page->data);
             if ($this->page['has_more']) {
                 $this->params = array_merge(
-                    $this->params ? $this->params : array(),
-                    array('starting_after' => $this->lastId)
+                    $this->params ?: [],
+                    ['starting_after' => $this->lastId]
                 );
                 $this->page = $this->page->all($this->params);
             } else {

+ 18 - 0
plugins/stripe/lib/Util/DefaultLogger.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * A very basic implementation of LoggerInterface that has just enough
+ * functionality that it can be the default for this library.
+ */
+class DefaultLogger implements LoggerInterface
+{
+    public function error($message, array $context = [])
+    {
+        if (count($context) > 0) {
+            throw new \Exception('DefaultLogger does not currently implement context. Please implement if you need it.');
+        }
+        error_log($message);
+    }
+}

+ 36 - 0
plugins/stripe/lib/Util/LoggerInterface.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * Describes a logger instance.
+ *
+ * This is a subset of the interface of the same name in the PSR-3 logger
+ * interface. We guarantee to keep it compatible, but we'd redefined it here so
+ * that we don't have to pull in the extra dependencies for users who don't want
+ * it.
+ *
+ * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
+ * for the full interface specification.
+ *
+ * The message MUST be a string or object implementing __toString().
+ *
+ * The message MAY contain placeholders in the form: {foo} where foo
+ * will be replaced by the context data in key "foo".
+ *
+ * The context array can contain arbitrary data, the only assumption that
+ * can be made by implementors is that if an Exception instance is given
+ * to produce a stack trace, it MUST be in a key named "exception".
+ */
+interface LoggerInterface
+{
+    /**
+     * Runtime errors that do not require immediate action but should typically
+     * be logged and monitored.
+     *
+     * @param string $message
+     * @param array $context
+     * @return null
+     */
+    public function error($message, array $context = []);
+}

+ 34 - 0
plugins/stripe/lib/Util/RandomGenerator.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace Stripe\Util;
+
+/**
+ * A basic random generator. This is in a separate class so we the generator
+ * can be injected as a dependency and replaced with a mock in tests.
+ */
+class RandomGenerator
+{
+    /**
+     * Returns a random value between 0 and $max.
+     *
+     * @param float $max (optional)
+     * @return float
+     */
+    public function randFloat($max = 1.0)
+    {
+        return mt_rand() / mt_getrandmax() * $max;
+    }
+
+    /**
+     * Returns a v4 UUID.
+     *
+     * @return string
+     */
+    public function uuid()
+    {
+        $arr = array_values(unpack('N1a/n4b/N1c', openssl_random_pseudo_bytes(16)));
+        $arr[2] = ($arr[2] & 0x0fff) | 0x4000;
+        $arr[3] = ($arr[3] & 0x3fff) | 0x8000;
+        return vsprintf('%08x-%04x-%04x-%04x-%04x%08x', $arr);
+    }
+}

+ 24 - 4
plugins/stripe/lib/Util/RequestOptions.php

@@ -6,10 +6,18 @@ use Stripe\Error;
 
 class RequestOptions
 {
+    /**
+     * @var array A list of headers that should be persisted across requests.
+     */
+    public static $HEADERS_TO_PERSIST = [
+        'Stripe-Account',
+        'Stripe-Version',
+    ];
+
     public $headers;
     public $apiKey;
 
-    public function __construct($key = null, $headers = array())
+    public function __construct($key = null, $headers = [])
     {
         $this->apiKey = $key;
         $this->headers = $headers;
@@ -32,6 +40,18 @@ class RequestOptions
         return $other_options;
     }
 
+    /**
+     * Discards all headers that we don't want to persist across requests.
+     */
+    public function discardNonPersistentHeaders()
+    {
+        foreach ($this->headers as $k => $v) {
+            if (!in_array($k, self::$HEADERS_TO_PERSIST)) {
+                unset($this->headers[$k]);
+            }
+        }
+    }
+
     /**
      * Unpacks an options array into an RequestOptions object
      * @param array|string|null $options a key => value array
@@ -45,15 +65,15 @@ class RequestOptions
         }
 
         if (is_null($options)) {
-            return new RequestOptions(null, array());
+            return new RequestOptions(null, []);
         }
 
         if (is_string($options)) {
-            return new RequestOptions($options, array());
+            return new RequestOptions($options, []);
         }
 
         if (is_array($options)) {
-            $headers = array();
+            $headers = [];
             $key = null;
             if (array_key_exists('api_key', $options)) {
                 $key = $options['api_key'];

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików