Skip to content
Snippets Groups Projects
Charge.php 7.87 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    namespace Civi\StripeImport;
    
    use Civi\Api4\Contact;
    use Civi\Api4\Contribution;
    use Civi\Api4\ContributionRecur;
    use Civi\Api4\Email;
    use Civi\Api4\FinancialTrxn;
    use Civi\Api4\StripeCustomer;
    use CRM_StripeImport_ExtensionUtil as E;
    
    class Charge extends Base {
    
      /**
       * @var string The Stripe charge ID
       */
      private string $stripeChargeID;
    
      public function setStripeChargeID(string $ChargeID) {
        $this->stripeChargeID = $ChargeID;
      }
    
      public function getStripeChargeID(): string {
        return $this->stripeChargeID;
      }
    
      /**
       * @var int The CiviCRM Contact ID
       */
      private int $contactID;
    
      public function setContactID(int $contactID) {
        $this->contactID = $contactID;
      }
    
      public function getContactID(): int {
        return $this->contactID;
      }
    
    
    mattwire's avatar
    mattwire committed
      public function hasContactID(): bool {
        return isset($this->contactID);
      }
    
    
      /**
       * @var int The CiviCRM Contribution ID
       */
      private int $contributionID;
    
      public function setContributionID(int $contributionID) {
        $this->contributionID = $contributionID;
      }
    
      public function getContributionID(): int {
        return $this->contributionID;
      }
    
      private string $description = '';
    
      public function setDescription(string $description) {
        $this->description = $description;
      }
    
      public function getDescription(): string {
        return $this->description;
      }
    
      private int $paymentInstrumentID;
    
      public function setPaymentInstrumentID(int $paymentInstrumentID) {
        $this->paymentInstrumentID = $paymentInstrumentID;
      }
    
      public function getPaymentInstrumentID(): int {
        return $this->paymentInstrumentID;
      }
    
      private int $financialTypeID;
    
      public function setFinancialTypeID(int $financialTypeID) {
        $this->financialTypeID = $financialTypeID;
      }
    
      public function getFinancialTypeID(): int {
        return $this->financialTypeID;
      }
    
    
      public function importOne(): array {
        // Retrieve the Stripe charge.
        $stripeCharge = $this->paymentProcessor->stripeClient->charges->retrieve($this->getStripeChargeID());
    
        // Get the related invoice.
        $stripeInvoiceID = \CRM_Stripe_Api::getObjectParam('invoice_id', $stripeCharge);
        if (!empty($stripeInvoiceID)) {
          $stripeInvoice = $this->paymentProcessor->stripeClient->invoices->retrieve($stripeCharge->invoice);
        }
    
        if (empty($this->getDescription())) {
          if (isset($stripeInvoice) && !empty(\CRM_Stripe_Api::getObjectParam('description', $stripeInvoice))) {
    
    mattwire's avatar
    mattwire committed
            $this->setDescription(\CRM_Stripe_Api::getObjectParam('description', $stripeInvoice) ?? '');
    
          }
        }
        if (empty($this->getDescription())) {
    
    mattwire's avatar
    mattwire committed
          $this->setDescription(\CRM_Stripe_Api::getObjectParam('description', $stripeCharge) ?? '');
    
        }
        if (empty($this->getDescription())) {
          $this->setDescription('Stripe: Manual import via API');
        }
    
    
    mattwire's avatar
    mattwire committed
        if (!$this->hasContactID()) {
    
          // Derive contact ID from stripe customer
          if (empty(\CRM_Stripe_Api::getObjectParam('customer_id', $stripeCharge))) {
            throw new \Exception('Missing customer_id in Stripe Charge. Cannot derive contact ID');
          }
          $stripeCustomer = \Civi\Api4\StripeCustomer::get(FALSE)
            ->addWhere('customer_id', '=', \CRM_Stripe_Api::getObjectParam('customer_id', $stripeCharge))
            ->addWhere('processor_id', '=', $this->getPaymentProcessorID())
            ->addClause('OR', ['currency', 'IS EMPTY'], ['currency', '=', \CRM_Stripe_Api::getObjectParam('customer_id', $stripeCharge)])
            ->addOrderBy('currency', 'DESC')
            ->execute()
            ->first();
          if (empty($stripeCustomer)) {
            throw new \Exception('Stripe Customer: ' . \CRM_Stripe_Api::getObjectParam('customer_id', $stripeCharge) . ' not found in CiviCRM. Cannot derive contact ID');
          }
          $this->setContactID($stripeCustomer['contact_id']);
        }
    
    
        // Check for a subscription.
        if (isset($stripeInvoice)) {
          $subscriptionID = \CRM_Stripe_Api::getObjectParam('subscription_id', $stripeInvoice);
          $contributionRecurID = NULL;
          if ($subscriptionID) {
            // Lookup the contribution_recur_id.
            $contributionRecur = ContributionRecur::get(FALSE)
              ->addWhere('trxn_id', '=', $subscriptionID)
              ->addWhere('is_test', '=', $this->paymentProcessor->getPaymentProcessor()['is_test'])
              ->execute()
              ->first();
            if (!$contributionRecur) {
              throw new \Exception(E::ts('The charge has a subscription, but the subscription is not in CiviCRM. Please import the subscription and try again.'));
            }
            $contributionRecurID = $contributionRecur['id'];
          }
        }
    
        // Prepare to either create or update a contribution record in CiviCRM.
        $contributionParams = [];
    
        // We update these parameters regardless if it's a new contribution
        // or an existing contributions.
        if (isset($stripeInvoice)) {
          $contributionParams['receive_date'] = \CRM_Stripe_Api::getObjectParam('receive_date', $stripeInvoice);
          $contributionParams['total_amount'] = \CRM_Stripe_Api::getObjectParam('total_amount', $stripeInvoice);
        }
        else {
          $contributionParams['receive_date'] = \CRM_Stripe_Api::getObjectParam('receive_date', $stripeCharge);
          $contributionParams['total_amount'] = \CRM_Stripe_Api::getObjectParam('total_amount', $stripeCharge);
        }
    
        // Check if a contribution already exists.
        if (empty($this->getContributionID())) {
          // See if we have a matching payment for this trxn_id and get the linked contribution ID
          $financialTrxn = FinancialTrxn::get(FALSE)
            ->addSelect('contribution.id')
            ->addJoin('Contribution AS contribution', 'LEFT', 'EntityFinancialTrxn')
            ->addWhere('is_payment', '=', TRUE)
            ->addWhere('trxn_id', '=', $this->getStripeChargeID())
            ->execute()
            ->first();
          if (!empty($financialTrxn['contribution.id'])) {
            $this->setContributionID($financialTrxn['contribution.id']);
          }
        }
    
        // If no contribution was found we need to create it
        if (empty($this->getContributionID())) {
          // We have to build all the parameters.
          $contributionParams['contact_id'] = $this->getContactID();
          $contributionParams['currency'] = \CRM_Stripe_Api::getObjectParam('currency', $stripeCharge);
          $contributionParams['trxn_id'] = $this->getStripeChargeID();
          $contributionParams['payment_instrument_id'] = $this->getPaymentInstrumentID();
          $contributionParams['financial_type_id'] = $this->getFinancialTypeID();
          $contributionParams['is_test'] = $this->paymentProcessor->getPaymentProcessor()['is_test'];
          $contributionParams['contribution_source'] = $this->getDescription();
          $contributionParams['contribution_status_id:name'] = 'Pending';
          if (!empty($contributionRecurID)) {
            $contributionParams['contribution_recur_id'] = $contributionRecurID;
          }
          $contribution = Contribution::create(FALSE)
            ->setValues($contributionParams)
            ->execute()
            ->first();
          $this->setContributionID($contribution['id']);
        }
    
        $webhookEventProcessor = new \Civi\Stripe\Webhook\Events($this->getPaymentProcessorID());
        $webhookEventProcessor->setData(\Stripe\StripeObject::constructFrom(['object' => $stripeCharge]));
        $return = $webhookEventProcessor->getResultObject();
        switch (\CRM_Stripe_Api::getObjectParam('status', $stripeCharge)) {
          case 'succeeded':
            $webhookEventProcessor->setEventType('charge.succeeded');
            $return = $webhookEventProcessor->doChargeSucceeded();
            break;
    
          case 'failed':
            $webhookEventProcessor->setEventType('charge.failed');
            $return = $webhookEventProcessor->doChargeFailed();
            break;
    
          case 'pending':
            // Nothing further to do (we already created the contribution and should not create a payment or failure yet)
            $return->ok = TRUE;
            $return->message = 'charge is pending';
        }
    
        return [
          'contribution_id' => $this->getContributionID(),
          'result' => $return,
        ];
      }
    
    }