SubscriptionGateway.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <?php
  2. namespace Braintree;
  3. use InvalidArgumentException;
  4. /**
  5. * Braintree SubscriptionGateway module
  6. *
  7. * <b>== More information ==</b>
  8. *
  9. * For more detailed information on Subscriptions, see {@link https://developers.braintreepayments.com/reference/response/subscription/php https://developers.braintreepayments.com/reference/response/subscription/php}
  10. *
  11. * PHP Version 5
  12. *
  13. * @package Braintree
  14. */
  15. class SubscriptionGateway
  16. {
  17. private $_gateway;
  18. private $_config;
  19. private $_http;
  20. public function __construct($gateway)
  21. {
  22. $this->_gateway = $gateway;
  23. $this->_config = $gateway->config;
  24. $this->_config->assertHasAccessTokenOrKeys();
  25. $this->_http = new Http($gateway->config);
  26. }
  27. public function create($attributes)
  28. {
  29. Util::verifyKeys(self::_createSignature(), $attributes);
  30. $path = $this->_config->merchantPath() . '/subscriptions';
  31. $response = $this->_http->post($path, ['subscription' => $attributes]);
  32. return $this->_verifyGatewayResponse($response);
  33. }
  34. public function find($id)
  35. {
  36. $this->_validateId($id);
  37. try {
  38. $path = $this->_config->merchantPath() . '/subscriptions/' . $id;
  39. $response = $this->_http->get($path);
  40. return Subscription::factory($response['subscription']);
  41. } catch (Exception\NotFound $e) {
  42. throw new Exception\NotFound('subscription with id ' . $id . ' not found');
  43. }
  44. }
  45. public function search($query)
  46. {
  47. $criteria = [];
  48. foreach ($query as $term) {
  49. $criteria[$term->name] = $term->toparam();
  50. }
  51. $path = $this->_config->merchantPath() . '/subscriptions/advanced_search_ids';
  52. $response = $this->_http->post($path, ['search' => $criteria]);
  53. $pager = [
  54. 'object' => $this,
  55. 'method' => 'fetch',
  56. 'methodArgs' => [$query]
  57. ];
  58. return new ResourceCollection($response, $pager);
  59. }
  60. public function fetch($query, $ids)
  61. {
  62. $criteria = [];
  63. foreach ($query as $term) {
  64. $criteria[$term->name] = $term->toparam();
  65. }
  66. $criteria["ids"] = SubscriptionSearch::ids()->in($ids)->toparam();
  67. $path = $this->_config->merchantPath() . '/subscriptions/advanced_search';
  68. $response = $this->_http->post($path, ['search' => $criteria]);
  69. return Util::extractAttributeAsArray(
  70. $response['subscriptions'],
  71. 'subscription'
  72. );
  73. }
  74. public function update($subscriptionId, $attributes)
  75. {
  76. Util::verifyKeys(self::_updateSignature(), $attributes);
  77. $path = $this->_config->merchantPath() . '/subscriptions/' . $subscriptionId;
  78. $response = $this->_http->put($path, ['subscription' => $attributes]);
  79. return $this->_verifyGatewayResponse($response);
  80. }
  81. public function retryCharge($subscriptionId, $amount = null, $submitForSettlement = false)
  82. {
  83. $transaction_params = ['type' => Transaction::SALE,
  84. 'subscriptionId' => $subscriptionId];
  85. if (isset($amount)) {
  86. $transaction_params['amount'] = $amount;
  87. }
  88. if ($submitForSettlement) {
  89. $transaction_params['options'] = ['submitForSettlement' => $submitForSettlement];
  90. }
  91. $path = $this->_config->merchantPath() . '/transactions';
  92. $response = $this->_http->post($path, ['transaction' => $transaction_params]);
  93. return $this->_verifyGatewayResponse($response);
  94. }
  95. public function cancel($subscriptionId)
  96. {
  97. $path = $this->_config->merchantPath() . '/subscriptions/' . $subscriptionId . '/cancel';
  98. $response = $this->_http->put($path);
  99. return $this->_verifyGatewayResponse($response);
  100. }
  101. private static function _createSignature()
  102. {
  103. return array_merge(
  104. [
  105. 'billingDayOfMonth',
  106. 'firstBillingDate',
  107. 'createdAt',
  108. 'updatedAt',
  109. 'id',
  110. 'merchantAccountId',
  111. 'neverExpires',
  112. 'numberOfBillingCycles',
  113. 'paymentMethodToken',
  114. 'paymentMethodNonce',
  115. 'planId',
  116. 'price',
  117. 'trialDuration',
  118. 'trialDurationUnit',
  119. 'trialPeriod',
  120. ['descriptor' => ['name', 'phone', 'url']],
  121. ['options' => [
  122. 'doNotInheritAddOnsOrDiscounts',
  123. 'startImmediately',
  124. ['paypal' => ['description']]
  125. ]],
  126. ],
  127. self::_addOnDiscountSignature()
  128. );
  129. }
  130. private static function _updateSignature()
  131. {
  132. return array_merge(
  133. [
  134. 'merchantAccountId', 'numberOfBillingCycles', 'paymentMethodToken', 'planId',
  135. 'paymentMethodNonce', 'id', 'neverExpires', 'price',
  136. ['descriptor' => ['name', 'phone', 'url']],
  137. ['options' => [
  138. 'prorateCharges',
  139. 'replaceAllAddOnsAndDiscounts',
  140. 'revertSubscriptionOnProrationFailure',
  141. ['paypal' => ['description']]
  142. ]],
  143. ],
  144. self::_addOnDiscountSignature()
  145. );
  146. }
  147. private static function _addOnDiscountSignature()
  148. {
  149. return [
  150. [
  151. 'addOns' => [
  152. ['add' => ['amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
  153. ['update' => ['amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
  154. ['remove' => ['_anyKey_']],
  155. ]
  156. ],
  157. [
  158. 'discounts' => [
  159. ['add' => ['amount', 'inheritedFromId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
  160. ['update' => ['amount', 'existingId', 'neverExpires', 'numberOfBillingCycles', 'quantity']],
  161. ['remove' => ['_anyKey_']],
  162. ]
  163. ]
  164. ];
  165. }
  166. /**
  167. * @ignore
  168. */
  169. private function _validateId($id = null) {
  170. if (empty($id)) {
  171. throw new InvalidArgumentException(
  172. 'expected subscription id to be set'
  173. );
  174. }
  175. if (!preg_match('/^[0-9A-Za-z_-]+$/', $id)) {
  176. throw new InvalidArgumentException(
  177. $id . ' is an invalid subscription id.'
  178. );
  179. }
  180. }
  181. /**
  182. * @ignore
  183. */
  184. private function _verifyGatewayResponse($response)
  185. {
  186. if (isset($response['subscription'])) {
  187. return new Result\Successful(
  188. Subscription::factory($response['subscription'])
  189. );
  190. } else if (isset($response['transaction'])) {
  191. // return a populated instance of Transaction, for subscription retryCharge
  192. return new Result\Successful(
  193. Transaction::factory($response['transaction'])
  194. );
  195. } else if (isset($response['apiErrorResponse'])) {
  196. return new Result\Error($response['apiErrorResponse']);
  197. } else {
  198. throw new Exception\Unexpected(
  199. "Expected subscription, transaction, or apiErrorResponse"
  200. );
  201. }
  202. }
  203. }
  204. class_alias('Braintree\SubscriptionGateway', 'Braintree_SubscriptionGateway');