Details
-
Type: Bug
-
Status: Closed
-
Priority: Major
-
Resolution: Fixed
-
Affects Version/s: None
-
Fix Version/s: 5.9.0-b1
-
Component/s: None
-
Labels:None
Description
PayPal checkout seems to create two separate transactions, one pending and one accepted.
To reproduce just make a payment with the webhooks configure.
Possible solution (replace text with language definitions)
Alright, in components/gateways/nonmerchant/paypal_checkout/paypal_checkout.php around line 240 replace the whole validate() section with
/** * Validates the incoming POST/GET response from the gateway to ensure it is * legitimate and can be trusted. * * @param array $get The GET data for this request * @param array $post The POST data for this request * @return array An array of transaction data, sets any errors using Input if the data fails to validate * - client_id The ID of the client that attempted the payment * - amount The amount of the payment * - currency The currency of the payment * - invoices An array of invoices and the amount the payment should be applied to (if any) including: * - id The ID of the invoice to apply to * - amount The amount to apply to the invoice * - status The status of the transaction (approved, declined, void, pending, reconciled, refunded, returned) * - reference_id The reference ID for gateway-only use with this transaction (optional) * - transaction_id The ID returned by the gateway to identify this transaction * - parent_transaction_id The ID returned by the gateway to identify this * transaction's original transaction (in the case of refunds) */ public function validate(array $get, array $post) { // Initialize API $api = $this->getApi($this->meta['client_id'], $this->meta['client_secret'], $this->meta['sandbox']); $payments = new PaypalCheckoutPayments($api); // Fetch webhook payload $payload = file_get_contents('php://input'); $webhook = json_decode($payload); // Discard all webhook events, except when the order is completed or approved $events = ['CHECKOUT.ORDER.APPROVED', 'PAYMENT.CAPTURE.COMPLETED']; if (!in_array($webhook->event_type ?? '', $events)) { $this->Input->setErrors(['unsupported_event' => ['unsupported_event' => 'Unsupported event type.']]); return; } $this->log('validate', json_encode($webhook), 'input', !empty($webhook)); // Capture payment if ($webhook->event_type == 'CHECKOUT.ORDER.APPROVED') { $orders = new PaypalCheckoutOrders($api); $response = $orders->capture(['id' => $webhook->resource->id]); $this->log('capture', json_encode($response->response()), 'output', empty($response->errors())); // Output errors if (($errors = $response->errors())) { $this->Input->setErrors($errors); return; } return [ 'client_id' => $webhook->resource->purchase_units[0]->custom_id ?? null, 'amount' => $webhook->resource->purchase_units[0]->amount->value ?? null, 'currency' => $webhook->resource->purchase_units[0]->amount->currency_code ?? null, 'invoices' => $this->unserializeInvoices($webhook->resource->purchase_units[0]->reference_id ?? ''), 'status' => 'pending', 'reference_id' => null, 'transaction_id' => $webhook->resource->id ?? null, 'parent_transaction_id' => null ]; } // Set the payment $payment = $webhook->resource ?? (object) []; // Fetch the transaction $order_response = (object) []; $order = (object) []; $transaction = (object) []; if (isset($payment->supplementary_data->related_ids->order_id)) { $orders = new PaypalCheckoutOrders($api); $order_response = $orders->get(['id' => $payment->supplementary_data->related_ids->order_id]) ?? (object) []; $order = $order_response->response(); $transaction = $order->purchase_units[0] ?? (object) []; } $this->log('validate', json_encode($transaction), 'output', !empty($transaction)); if (empty($transaction)) { $this->Input->setErrors(['missing_transaction' => ['missing_transaction' => 'No transaction found.']]); return; } // Set status $status = 'error'; $success = false; switch ($payment->status ?? 'ERROR') { case 'COMPLETED': $status = 'approved'; $success = true; break; case 'APPROVED': $status = 'pending'; $success = true; break; case 'VOIDED': $status = 'void'; $success = true; break; } if (!$success) { return; } // Output errors if (($errors = $order_response->errors())) { $this->Input->setErrors($errors); return; } return [ 'client_id' => $transaction->custom_id ?? null, 'amount' => $payment->amount->value ?? null, 'currency' => $payment->amount->currency_code ?? null, 'invoices' => $this->unserializeInvoices($transaction->reference_id ?? ''), 'status' => $status, 'reference_id' => $payment->id ?? null, 'transaction_id' => $order->id ?? null, 'parent_transaction_id' => null ]; }