Bagisto Forum

    Bagisto

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    Custom PhonePe Payment Gateway: Cart available on redirect but null in verify method

    General Discussion
    2
    2
    86
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • A
      arunchahar last edited by

      Hi community,

      I’m integrating a custom PhonePe payment gateway in Bagisto v2.3. The integration redirects to PhonePe successfully and the cart is correctly available during the initial payment request.

      However, when the customer is redirected back for verification (after successful payment), the cart becomes null in the verify method. This causes the order placement to fail.

      Below is my code for both the redirect and verify methods in the PhonepeController

          public function redirect(Request $request)
          {
              try {
                  $cart = Cart::getCart();
      
                  if (!$cart) {
                      throw new \Exception('Cart not found');
                  }
      
                  $billingAddress = $cart->billing_address;
      
                  if (!$billingAddress || !$billingAddress->phone) {
                      throw new \Exception('Billing address or phone number missing');
                  }
      
                  $shipping = $cart->selected_shipping_rate ? $cart->selected_shipping_rate->price : 0;
                  $discount = $cart->discount_amount;
                  $amount = ($cart->sub_total + $cart->tax_total + $shipping) - $discount;
      
                  $orderId = 'order_' . $cart->id . '_' . time();
      
                  $merchantId = core()->getConfigData('sales.payment_methods.phonepe.merchant_id');
                  $saltKey = core()->getConfigData('sales.payment_methods.phonepe.salt_key');
                  $saltIndex = core()->getConfigData('sales.payment_methods.phonepe.salt_index');
                  $env = core()->getConfigData('sales.payment_methods.phonepe.env'); // 'sandbox' or 'production'
      
                  // Validate required configuration
                  if (empty($merchantId) || empty($saltKey) || empty($saltIndex)) {
                      throw new \Exception('PhonePe payment configuration is incomplete');
                  }
      
                  $callbackUrl = route('phonepe.verify') . '?order_id=' . $orderId;
      
                  $payload = [
                      "merchantId" => $merchantId,
                      "merchantTransactionId" => $orderId,
                      "merchantUserId" => auth()->id() ?? 'guest_' . $billingAddress->phone,
                      "amount" => intval($amount * 100), // in paise
                      "redirectUrl" => $callbackUrl,
                      "redirectMode" => "POST",
                      "callbackUrl" => $callbackUrl,
                      "mobileNumber" => $billingAddress->phone,
                      "paymentInstrument" => [
                          "type" => "PAY_PAGE"
                      ]
                  ];
      
                  $base64Payload = base64_encode(json_encode($payload));
                  $checksum = hash('sha256', $base64Payload . "/pg/v1/pay" . $saltKey) . "###" . $saltIndex;
      
                  $url = $env === 'sandbox'
                      ? "https://api-preprod.phonepe.com/apis/pg-sandbox/pg/v1/pay"
                      : "https://api.phonepe.com/apis/hermes/pg/v1/pay";
      
                  $response = Http::withHeaders([
                      'Content-Type' => 'application/json',
                      'X-VERIFY' => $checksum,
                      'Accept' => 'application/json',
                  ])->post($url, [
                      "request" => $base64Payload
                  ]);
      
                  if (!$response->successful()) {
                      throw new \Exception('PhonePe API request failed: ' . $response->status());
                  }
      
                  $responseData = $response->json();
      
                  // Log the response for debugging
                  \Log::info('PhonePe Payment Init Response:', $responseData);
      
                  if (isset($responseData['success']) && $responseData['success'] === true &&
                      isset($responseData['data']['instrumentResponse']['redirectInfo']['url'])) {
                      $request->session()->put('phonepe_order_id', $orderId);
                      return redirect()->to($responseData['data']['instrumentResponse']['redirectInfo']['url']);
                  }
      
                  $errorMessage = $responseData['message'] ?? 'Unable to initiate payment';
                  throw new \Exception($errorMessage);
      
              } catch (\Exception $e) {
                  \Log::error('PhonePe Payment Error: ' . $e->getMessage());
                  session()->flash('error', $e->getMessage());
                  return redirect()->route('shop.checkout.cart.index');
              }
          }
      
          public function verify(Request $request)
          {
              try {
                  // Log the full incoming request to debug what's coming from PhonePe
                 // \Log::info('PhonePe Verify Incoming Request:', $request->all());
      
                  // Get the merchant transaction ID (i.e., your order ID)
                  $orderId = $request->input('order_id') ?? $request->get('order_id');
      
                  if (!$orderId) {
                      session()->flash('error', 'PhonePe payment verification failed: Missing order ID');
                      return redirect()->route('shop.checkout.cart.index');
                  }
      
                  // Fetch configuration values
                  $merchantId = core()->getConfigData('sales.payment_methods.phonepe.merchant_id');
                  $saltKey    = core()->getConfigData('sales.payment_methods.phonepe.salt_key');
                  $saltIndex  = core()->getConfigData('sales.payment_methods.phonepe.salt_index');
                  $env        = core()->getConfigData('sales.payment_methods.phonepe.env');
      
                  // Determine base URL based on environment
                  $baseUrl = $env === 'sandbox'
                      ? 'https://api-preprod.phonepe.com/apis/pg-sandbox/pg/v1/status'
                      : 'https://api.phonepe.com/apis/hermes/pg/v1/status';
      
                  // Create the request path
                  $path = "/pg/v1/status/{$merchantId}/{$orderId}";
                  $statusUrl = "{$baseUrl}/{$merchantId}/{$orderId}";
      
                  // Create the checksum
                  $checksum = hash('sha256', $path . $saltKey) . "###" . $saltIndex;
      
                  // Log the constructed URL and checksum
                  \Log::info('PhonePe Status URL: ' . $statusUrl);
                  \Log::info('PhonePe Checksum: ' . $checksum);
      
                  // Make the request to PhonePe status API
                  $response = Http::withHeaders([
                      'Content-Type' => 'application/json',
                      'X-VERIFY'     => $checksum,
                      'Accept'       => 'application/json',
                  ])->get($statusUrl);
      
                  // Log raw response for debugging
                  \Log::info('PhonePe Status API Raw Response: ' . $response->body());
      
                  if (!$response->successful()) {
                      session()->flash('error', 'PhonePe verification failed. Try again later.');
                      return redirect()->route('shop.checkout.cart.index');
                  }
      
                  $data = $response->json();
      
                  // Log parsed response
                  \Log::info('PhonePe Status API Parsed Response:', $data);
      
                  if (
                      isset($data['success']) && $data['success'] === true &&
                      isset($data['code']) && $data['code'] === 'PAYMENT_SUCCESS' &&
                      isset($data['data']['state']) && in_array($data['data']['state'], ['SUCCESS', 'COMPLETED']) &&
                      isset($data['data']['merchantTransactionId']) && $data['data']['merchantTransactionId'] === $orderId
                  ) {
      
                      $cart = Cart::getCart(); // Retrieve the cart
      
                      $order = $this->orderRepository->create((new OrderResource($cart))->jsonSerialize());
                      $this->orderRepository->update(['status' => 'processing'], $order->id);
      
                      if ($order->canInvoice()) {
                          $this->invoiceRepository->create($this->prepareInvoiceData($order));
                      }
      
                      Cart::deActivateCart();
                      session()->flash('order_id', $order->id);
      
                      return redirect()->route('shop.checkout.onepage.success');
                  }
      
                  session()->flash('error', 'PhonePe payment failed or cancelled.');
                  return redirect()->route('shop.checkout.cart.index');
      
              } catch (\Exception $e) {
                  \Log::error('PhonePe Verify Exception: ' . $e->getMessage());
                  session()->flash('error', 'Something went wrong during payment verification.');
                  return redirect()->route('shop.checkout.cart.index');
              }
          }
      
      1 Reply Last reply Reply Quote 0
      • Rishabh-Webkul
        Rishabh-Webkul last edited by

        Hello @arunchahar

        For this, we will need to review your entire package to see what changes you have made and where. To do that, you can purchase support hours for this.

        https://store.webkul.com/bagisto-hourly-customization-package.html

        Regards
        Team Bagisto

        1 Reply Last reply Reply Quote 0
        • First post
          Last post