BxPaymentProviderAppleInApp.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Payment Payment
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. class BxPaymentProviderAppleInApp extends BxBaseModPaymentProvider implements iBxBaseModPaymentProvider
  12. {
  13. protected $_sSecret;
  14. protected $_sVerifyReceipt;
  15. protected $_sVerifyReceiptSandbox;
  16. public function __construct($aConfig)
  17. {
  18. $this->MODULE = 'bx_payment';
  19. parent::__construct($aConfig);
  20. $this->_sVerifyReceipt = 'https://buy.itunes.apple.com/verifyReceipt';
  21. $this->_sVerifyReceiptSandbox = 'https://sandbox.itunes.apple.com/verifyReceipt';
  22. }
  23. public function initOptions($aOptions)
  24. {
  25. parent::initOptions($aOptions);
  26. $this->_sSecret = $this->getOption('secret');
  27. }
  28. public function initializeCheckout($iPendingId, $aCartInfo, $sRedirect = '')
  29. {
  30. return $this->_sLangsPrefix . 'err_not_supported';
  31. }
  32. public function finalizeCheckout(&$aData)
  33. {
  34. return array('code' => 1, 'message' => $this->_sLangsPrefix . 'err_not_supported');
  35. }
  36. public function notify()
  37. {
  38. $iResult = $this->_processNotification();
  39. http_response_code($iResult);
  40. }
  41. public function getMenuItemsActionsRecurring($iClientId, $iVendorId, $aParams = array())
  42. {
  43. if(empty($aParams['id']))
  44. return array();
  45. $sPrefix = 'bx-payment-aina-';
  46. return array(
  47. array('id' => $sPrefix . 'cancel', 'name' => $sPrefix . 'cancel', 'class' => '', 'link' => 'https://apps.apple.com/account/subscriptions', 'target' => '_blank', 'title' => _t('_bx_payment_aina_menu_item_title_cancel'))
  48. );
  49. }
  50. protected function _processNotification()
  51. {
  52. $sPath = "php://input"; //BX_DIRECTORY_PATH_TMP . "aina_renew.json"; // "aina_init.json";
  53. $sInput = @file_get_contents($sPath);
  54. $aNotification = json_decode($sInput, true);
  55. if(empty($aNotification) || !is_array($aNotification))
  56. return 404;
  57. $sType = $aNotification['notification_type'];
  58. if(!in_array($sType, array('INITIAL_BUY_CUSTOM', 'DID_RENEW', 'REFUND', 'CANCEL')))
  59. return 200;
  60. $this->log($aNotification, 'Webhooks: ' . (!empty($sType) ? $sType : ''), BX_LOG_INFO);
  61. $sMethod = '_processNotification' . bx_gen_method_name(strtolower($sType), array('.', '_', '-'));
  62. if(!method_exists($this, $sMethod))
  63. return 200;
  64. return $this->$sMethod($aNotification) ? 200 : 403;
  65. }
  66. protected function _processNotificationInitialBuyCustom($aNotification)
  67. {
  68. if(!$this->_verifyNotification(array('rt' => $aNotification['original_data']['transactionReceipt'], 'pt' => $aNotification['product'], 'tx' => $aNotification['transaction'], 'txo' => $aNotification['original_transaction'])))
  69. return false;
  70. $sOrder = $aNotification['transaction'];
  71. if(!empty($aNotification['original_transaction']))
  72. $sOrder = $aNotification['original_transaction'];
  73. $sOrder = bx_process_input($sOrder);
  74. $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'order', 'order' => $sOrder));
  75. if(!empty($aPending) && is_array($aPending) && (int)$aPending['processed'] == 1)
  76. return false;
  77. $sModule = bx_process_input($aNotification['module']);
  78. $aModule = $this->_oModule->_oDb->getModuleByName($sModule);
  79. if(empty($aModule) || !is_array($aModule))
  80. return false;
  81. $iClient = (int)$aNotification['profile_id'];
  82. $aInfo = $this->_oModule->getObjectCart()->getInfo(BX_PAYMENT_TYPE_RECURRING, $iClient, $this->_iVendor, array(
  83. array($this->_iVendor, $aModule['id'], bx_process_input($aNotification['product']), (int)$aNotification['count'])
  84. ));
  85. if(empty($aInfo) || $aInfo['vendor_id'] == BX_PAYMENT_EMPTY_ID || empty($aInfo['items']))
  86. return false;
  87. $iPendingId = $this->_oModule->_oDb->insertOrderPending($iClient, BX_PAYMENT_TYPE_RECURRING, $this->_sName, $aInfo);
  88. if(empty($iPendingId))
  89. return false;
  90. $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
  91. $aSubscription = array(
  92. 'customer_id' => '',
  93. 'subscription_id' => $sOrder,
  94. 'status' => BX_PAYMENT_SBS_STATUS_ACTIVE
  95. );
  96. if(!$this->_oModule->getObjectSubscriptions()->register($aPending, $aSubscription))
  97. return false;
  98. $this->_oModule->_oDb->updateOrderPending($iPendingId, array(
  99. 'order' => $sOrder,
  100. 'error_code' => 0,
  101. 'error_msg' => _t('_bx_payment_aina_msg_subscribed')
  102. ));
  103. return $this->_oModule->registerPayment($iPendingId);
  104. }
  105. protected function _processNotificationDidRenew(&$aNotification)
  106. {
  107. $aRenew = array_shift($aNotification['unified_receipt']['pending_renewal_info']);
  108. if(!$this->_verifyNotification(array('rt' => $aNotification['unified_receipt']['latest_receipt'], 'pt' => $aRenew['product_id'], 'txo' => $aRenew['original_transaction_id'])))
  109. return false;
  110. $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'order', 'order' => $aRenew['original_transaction_id']));
  111. if(empty($aPending) || !is_array($aPending))
  112. return false;
  113. if($aPending['type'] == BX_PAYMENT_TYPE_RECURRING)
  114. $this->_oModule->getObjectSubscriptions()->prolong($aPending);
  115. return $this->_oModule->registerPayment($aPending);
  116. }
  117. protected function _processNotificationRefund(&$aNotification)
  118. {
  119. //TODO: We need to get 'PendingId' to process refund.
  120. }
  121. protected function _processNotificationCancel(&$aNotification)
  122. {
  123. //TODO: We need to get 'PendingId' to cancel the subscription.
  124. }
  125. protected function _verifyNotification($aParams)
  126. {
  127. $aRequest = array(
  128. 'receipt-data' => $aParams['rt'],
  129. 'password' => $this->_sSecret
  130. );
  131. $sInput = bx_file_get_contents($this->_sVerifyReceipt, $aRequest, 'post-json');
  132. $aInput = json_decode($sInput, true);
  133. if(isset($aInput['status']) && (int)$aInput['status'] == 21007) {
  134. $sInput = bx_file_get_contents($this->_sVerifyReceiptSandbox, $aRequest, 'post-json');
  135. $aInput = json_decode($sInput, true);
  136. }
  137. if(!empty($aInput['latest_receipt_info']) && is_array($aInput['latest_receipt_info']))
  138. foreach($aInput['latest_receipt_info'] as $aRecipt)
  139. if($aRecipt['product_id'] == $aParams['pt'] && ((isset($aParams['txo']) && $aRecipt['original_transaction_id'] == $aParams['txo']) || (isset($aParams['tx']) && $aRecipt['transaction_id'] == $aParams['tx'])))
  140. return true;
  141. return false;
  142. }
  143. }
  144. /** @} */