Paypal SPB headless in shopware 6.5 with storefront-ui

Ich versuche seit einiger Zeit in einer SPA Paypal smart Buttons zu integrieren. Allerdings mit dem Erfolg , dass ich immer eine Redirect URL für erhalte die auf Paypal checkoutnow führt.
Hat es jemand bereits geschafft mit der 6.5 Version eine andere Bezahlmethode zum laufen zu bringen.
(Giropay…)
Die custom_fields bei order transactions werden bei mir nur gefüllt wenn ich den Endpunkt handle-payment aufrufe aber übergaben von den Paypal Transaktionsdaten die ich versuche mitzugeben werden ignoriert. Stattdessen scheint intern eine neue Transaktion getriggert zu werden welche Daten dann in der Tabelle order_transaction inserted werden.
Es wird der spb Fall anscheinend nicht berücksichtigt.

Reihenfolge ist wie folgt

  1. in Paypal createOrder führe ich den (POST)Endpunkt store-api/paypal/create-order. Momentan mit der Übergabe von einem Json { „tos“:„on“, „product“:„spb“ } hatte aber auch multipart/form versucht.
  2. in Paypal onApprove store-api/checkout/order mit (POST ) ausführen übergebe momentan ein JSON „transactions“:{
    „custom_fields“:{
    „payerID“: data.payerID,
    „swag_paypal_order_id“:data.paymentID,
    }
  3. refrashCart
  4. „/store-api/handle-payment“ (POST) mit einem Json {
    orderId: orderCreated.id,
    successUrl: ${window.location.origin}/order/success?order=${orderCreated.id}&success=true,
    errorUrl: ${window.location.origin}/order/success?order=${orderCreated.id}&success=false,
    „paymentDetails“:paymentDetails
    },
    wobei paymentDetails ebenfalls ein JSON ist transactions:{
    custom_fields:{
    „payerID“: data.payerID,
    „swag_paypal_order_id“:data.paymentID,
    „paymentSource“:data.paymentSource,
    „facilitatorAccessToken“:data.facilitatorAccessToken,
    „isPayPalSpbCheckout“:true
    }
    }

Worauf ganz andere Daten in der order_transaction stehen .

Falls jemand eine Idee hat wäre toll. Blicke irgebndwie auch nicht ganz durch. Die Beschreibung bei SwagPaypal ist eher nicht vorhanden. Früher gab es mal wohl eine SPBCheckouRoute die nicht mehr vorhanden ist, oder ich finde diese nicht. Dei SwagPaypal Version ist 7.2.4.

Danke für alle Hinweise

Habe es nach einigen versuchen dann geschafft.

Hey @adam1 - wie konntest du es dann lösen? Bin ebenfalls an einer headless implementierung dran :blush:

Hallo habe „@paypal/paypal-js“: „^7.0.3“ installiert.

dann eine eigene Komponente für den Paypal Bereich hinzugefügt.

Der Code von meinem PAypal Container in verkürzter Form:

Entscheidend ist der PayPal - Bereich. Die Funktion createOrder (invoke beachten) . Ich hole noch die PaypalPaymentId. Wäre ev. nicht unbedingt nötig, da diese bereits beim Auswählen von Paypal richtig gesetzt wird. Beim auswählen von Paypal blende ich den normalen Bezahlbutton aus und die Paypal Smart Buttons werden eingeblendet. Hoffe der Code hilft.

<template>
  <div id="paypal-button-container" class="paypal-button-container" />
</template>
<script>
import {
  CreateOrderActions,
  CreateOrderData,
  loadScript,
  OnApproveActions,
  OnApproveData,
} from "@paypal/paypal-js";
import { computed, ref} from "@vue/composition-api"
import { handlePayment } from "@shopware-pwa/shopware-6-client"
import {
PAGE_CHECKOUT,
PAGE_ORDER_SUCCESS,
PAGE_ORDER_PAYMENT_FAILURE,
} from "@/helpers/pages"
import {
getApplicationContext,
useCheckout,
useSessionContext,
useCart,
} from "@shopware-pwa/composables"
import FormData from "form-data";
export default {
  setup(props, { emit }) {
    const { paymentMethod, setPaymentMethod } = useSessionContext()
    const { createOrder:invokeCreateOrder, loadings, paymentMethods, getPaymentMethods } = useCheckout()
    const { refreshCart,cart } = useCart();
    const errormessage =ref([]);
    const EMassege = computed(() => errormessage.value )
    const { apiInstance,routing } =
    getApplicationContext({
      contextName: "PaypalContainer",
    })
    const orderCreated = ref();
    const redirectPaymentUrl = ref();
    let customerComment = "";
    const paypalMethod = computed(() => {
      return paymentMethods.value?.find(
        (method) => method.shortName === "pay_pal_payment_handler",
      );
    });
    async function createOrder(data) {
        // 1. place an order
      const orderCreated = await invokeCreateOrder({
      });
      refreshCart();
      const paymentDetails = {
          custom_fields:{
            "payerID": data.payerID,
            "swag_paypal_order_id":data.paymentID,    
        }
      }
      const finishUrl = routing.getAbsoluteUrl(
                `${PAGE_ORDER_SUCCESS}?orderId=${orderCreated.id}`
              )
              const errorUrl = routing.getAbsoluteUrl(
                `${PAGE_ORDER_PAYMENT_FAILURE}?orderId=${orderCreated.id}`
              )
        
      const queryPart = "?orderId="+orderCreated.id+"&finishUrl="+finishUrl
      const url = "/store-api/handle-payment"+ queryPart;
      const handledPaymentResponse = await apiInstance.invoke.post(
          url,
          {
            "isPayPalSpbCheckout":true,
            "paymentDetails":paymentDetails,
            "paypalOrderId":data.paymentID
          },
        );
        const redirectPaymentUrl = handledPaymentResponse?.data?.redirectUrl;
        // perform a redirection to the external payment gateway
        window.location.href = redirectPaymentUrl              
      }
      let paypal;
      const resp = (async () => {
          try {
              paypal = await loadScript({ clientId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
              currency: "EUR",
              dataPageType: "checkout",
              commit:true,
              locale:"de_DE",
              intent:"capture"
          });
              
          } catch (error) {
              console.error("failed to load the PayPal JS SDK script", error);
          }
          if (paypal) {
            try {
                await paypal.Buttons(
                  {
                    createOrder: async (
                      data,
                      actions
                    ) => {
                      await setPaymentMethod(paypalMethod.value);
                      const formData = new FormData();
                            formData.append('product', 'spb');
                      const jsonData = {
                        "product":"spb"
                      }
                        const response = await apiInstance.invoke.post(
                          "https://www.YourShopware6Server.de/store-api/paypal/create-order",jsonData
                        );
                        return response?.data?.token;
                    },
                  onClick: function() {
                  // Finalize the transaction after payer approval
                  onApprove: async (data, actions) => {
                      orderCreated.value = await createOrder(data);
                    },
                  }).render("#paypal-button-container");
        } catch (error) {
            console.error("failed to render the PayPal Buttons", error);
        }
      }
    })();
        return { EMassege,errormessage, paymentMethods, paypalMethod, cart, cartErrors}
  }
}
</script>

@adam1 - Vielen Dank, das war der einzig wirklich hilfreiche und funktionierende Beitrag den ich bisher gefunden habe :rocket:

Paypal öffnet sich im Popup-Window. Zahlung wird vom Nutzer approved.

Dann im onApprove wird die Order erstellt und handle-payment aufgerufen. Hier bekomme ich die redirect-URL zur Finalisierung. Die geht aber über die Shopware-Instanz und leitet dann wieder auf mein Frontend (finishURL) weiter.

Das will ich noch anpassen, dass die Finalisierung dann auch über die api läuft. Shopware ist bei uns im privaten Netzwerk und von außen so garnicht erreichbar.

Aber ich denke das sollte jetzt easy sein.

Noch mal danke, das hat echt geholfen. Die einzige Doku dazu ist echt nur der Quellcode… Das ist schon ein bisschen Schade :sweat_smile: