PayPal-Zahlung über REST API abschließen

Hallo zusammen,

ich arbeite gerade an einer Shopware-Instanz mit Headless-Funktion, d.h. alle Aktionen gehen über die REST API.

Bei der Integration des PayPal-Plugins im Backend mit den Smart Payment-Buttons von PayPal im Frontend komme ich leider nicht mehr weiter. Es ist mir zwar gelungen, die Bestellung im Backend anzulegen, die Zahlung über PayPal durchzuführen (mithilfe eines vorhergehenden Aufrufs von /store-api/v3/paypal/spb/create-order um eine ID zu erzeugen) und den Webhook des Plugins zu nutzen, sodass die Bestellung als “bezahlt” markiert wird.

Was jedoch nicht funktioniert, ist, dass die beiden Custom Fields **swag\_paypal\_token** und **swag\_paypal\_transaction\_id** in der **order-transaction** gesetzt werden. Das führt leider dazu, dass in der Admin-Oberfläche keine Details zur PayPal-Zahlung abgerufen werden können.

Wer hat einen Tipp, welche Route des PayPal-Plugins ich nutzen kann, um die Custom Fields nach der Zahlung zu setzen?

Bin für jeden Hinweis sehr dankbar :slight_smile:

Lg Mike

PS: das hier scheint die Stelle zu sein, an der die Custom Fields gesetzt werden: SwagPayPal/EcsSpbHandler.php at 9329ccffff8e0824c6b094ad8fafbe30ffc7f180 · shopwareLabs/SwagPayPal · GitHub

Ich finde leider die dazugehörige Route nicht :frowning:

Hallo Mike,

um die Bezahlung komplett abzuschließen, solltest du noch die HandlePaymentMethodRoute aufrufen. Diese Route sorgt wiederum dafür dass PayPalPaymentHandler::pay aufgerufen wird. Hierbei ist wichtig, dass du den Parameter „isPayPalSpbCheckout“ mitsendest, Dieser wird hier gebraucht, um eine Smart Payment Button Zahlung zu erkennen. Anschließend wird der schon von dir genannte EcsSpbHandler aufgerufen. Dieser benötigt noch „paypalOrderId“ als Parameter, wo du die ID der aktuellen PayPal-Order mitsenden musst. Danach sollte das auch korrekt in der DB abgespeichert sein.

Meine Aussagen beziehen sich jetzt auf die aktuellste Version des PayPal Plugins (2.0.2).

Viele Grüße aus Schöppingen

cool Michael Telgmann

2 „Gefällt mir“

Hallo Michael,

besten Dank für deine ausführliche Antwort! Das hat mich der Lösung ein großes Stück näher gebracht :slight_smile:

Nachdem ich **/handle-payment** mit den erforderlichen Parametern aufrufe, werden die beiden Custom Fields gesetzt und ich erhalte eine redirectUrl zurück. Diese Redirect URL ist jedoch nicht immer gleich. Sie führt manchmal zu https://** sandbox.paypal.com/checkoutnow und manchmal zu http:// **localhost/payment/finalize-transaction

Ich konnte bisher nicht herausfinden, wann welche URL zurückgegeben wird.

Jenachdem, welche URL zurückgegeben wird, passiert folgendes:

  • wenn ich der URL zu paypal.com  folge, werde ich erneut aufgefordert auf “Jetzt bezahlen” zu klicken (obowhl das vorher schon passiert ist). Anschließend wird man über  http://localhost/payment/finalize-transaction  (302 FOUND) zur  finishUrl  weitergeleitet, die beim Aufruf von  /handle-payment  mitgegeben wurde.
  • wenn ich (direkt) der URL zu  /finalize-transaction  (inkl. Parameter) folge, wird ein 404 NOT FOUND geschmissen:

Der zusätzliche Schritt über paypal.com/checkoutnow erscheint mir nicht richtig, da der Kunde zuvor schon auf “Jetzt bezahlen” geklickt hat und man das Payment theoretisch auch im Frontend (ohne  paypal.com/checkoutnow  aufzurufen) über actions.order.capture() einziehen könnte.

Wenn ich mich nicht täusche, erfolgt im Demoshop auch kein zweiter Redirect zu paypal.com nach dem Aufruf von /handle-payment. Hast du eine Idee, was bei meinem Prozess noch fehlt/falsch ist?

//UPDATE #1:  okay, das Rätsel um die verschiedenen Redirect URLs scheint gelöst: der Request zu  /handle-payment  sollte ein POST sein, wobei einige der Parameter (z.B. der isPayPalSpbCheckout ) im Body und andere (z.B. die orderId ) im Query String erwartet werden. Um diese “Falle” zu vermeiden führe ich nun einen POST mit allen Parametern als Query String und gleichzeitig im Body durch. Dann wird stets die Redirect URL zu  /finalize-transaction  zurückgeliefert.

Fraglich ist noch, weshalb diese zu dem o.g. 404 Not Found führt

//UPDATE #2 : ich bin fälschlicherweise davon ausgegangen, dass der “Payment Capture”-Prozess serverseitig bei  /finalize-transaction  ausgeführt wird. Das scheint aber nicht der Fall zu sein. Wenn ich das Capture im Frontend über **actions.order.capture() **durchführe, ist der Request zu  /finalize-transaction vermutlich   nicht mehr notwendig. Die Bestellung befindet sich (durch den Webhook) im Status paid und die Details zur Transaktion werden (durch den Request zu /handle-payment ) im PayPal-Tab angezeigt. Also alles wie es sein sollte :slight_smile:

1 „Gefällt mir“

Hallo Mike,

genau, im Prinzip machen wir das genauso, nur über die Storefront. Nachdem die „handle-payment“ Route aufgerufen ist, kannst du einfach zur Finish Seite weiterleiten  Thumb-Up

Viele Grüße aus Schöppingen

cool Michael Telgmann

PS: übrigens schön zu sehen, dass das PayPal Plugin auch in einem Headless Shop funktioniert  Smile 

Hallo Michael und Mike,

ich stehe aktuell vor dem gleichen Problem wie Mike. Also das PayPal Plugin bei einem Headless Shop zum funktionieren zu bringen :slight_smile: Ich fand diesen thread schon sehr hilfreich, danke an dieser Stelle schonmal. Bei mir ist es aktuell so, dass ich eine Bestellung durchgeführt bekomme, aber dann im Backend der Zahlungsstatus als “in Bearbeitung” steht. Die Reihenfolge in der ich meine Aufrufe durchführe ist die folgende —> /paypal/spb/create-order —> bezahlen —> /checkout/order ----> handle-payment. Ich befürchte das an der Reihenfolge etwas nicht stimmt. Das zweite Problem das ich habe ist, dass ich nicht weiß, was für einen Wert ich dem Parameter isPayPalSpbCheckout geben soll. Wär super wenn ihr (oder irgendjemand anderes) mir da irgendwie weiterhelfen könntet :slight_smile:

Grüße

Lukas

Hallo Lukas,

den Parameter „isPayPalSpbCheckout“ einfach den Wert „true“ übergeben. Dadurch wird im PayPal Payment Handler der Workflow für die Smart Payment Buttons ausgelöst.

Viele Grüße aus Schöppingen

cool Michael Telgmann

2 „Gefällt mir“

Hallo Michael,

danke für deine schnelle Antwort. Ich versteh leider immernoch nicht so ganz wie ich eine Bestellung auf bezahlt setzen kann, muss eine Bestellung vor der Bezahlung erstellt werden? Bzw. muss ich die handle-payment route vor der Bezahlung aufrufen, oder wie versteht shopware dass die Bezahlung vernünftig durchgeführt wurde? Wenn ich das richtig verstanden habe muss ich irgendwas mit der Webhook machen? Foot-in-Mouth

Mit freundlichen Grüßen

Lukas

Halo Lukas,

du möchtest aber schon die Smart Payment Buttons einbinden oder? 
Hier siehst du wie wir das gemacht haben: SwagPayPal/swag-paypal.smart-payment-buttons.js at master · shopwareLabs/SwagPayPal · GitHub
Hier kommen die Daten her: SwagPayPal/SPBCheckoutDataService.php at master · shopwareLabs/SwagPayPal · GitHub
Mit Webhooks musst du nichts machen.

Viele Grüße aus Schöppingen

cool Michael Telgmann

Hallo in die Runde!

Ich hänge leider auch bei dem Paypal/Headless-Prozess. Vielleicht hat jemand einen Tipp/Anschub! :wink:

Ich habe SW6 als Headless-Variante umgesetzt. Im Falle einer Paypal-Zahlungsmethode starte ich den Paypal-Prozess indem ich über die Paypal-eigene API die Zahlung erstelle, im Erfolgsfall erstelle ich die Bestellung über die SW6-Api (checkout/order) und führe dann die Zahlung via Paypal-API aus. Als Rückgabe erhalte ich die Zahlungs-ID sowie das Token zur Paypal-Zahlung. Wie bekomme ich nun diese Informationen in die entsprechende Bestellung?

Ich befürchte, dass die Implementierung der Zahlung über das Paypal-Plugin erfolgen muss (nicht über eine eigene Implementierung über die Paypal-API). Hat jemand Informationen oder Ressourcen, wie das Paypal-Plugin über die Rest-API von SW6 genutzt werden soll/muss? Gibt es eine Dolumentation dazu?

1000000 Dank im Voraus!
Max

Hi @maxmuetze

du hast es zwischenzeitlich vielleicht schon selbst gelöst, ich lass mein Kommentar trotzdem mal hier:

ich nehme an du hast im Frontend die Smart Payment Buttons (SPB) von PayPal, richtig? Bei den Buttons sieht man häufig die client-seitige Zahlungsabwicklung:

paypal.Buttons({
    // Set up the transaction (client-side)
    createOrder: function(data, actions) {
        return actions.order.create({
            purchase_units: [{
                amount: {
                    value: '88.44'
                }
            }]
         });
     },
     // etc...
}).render('#paypal-button-container');

Wir wollen die PayPal-Zahlung aber über Shopware abwickeln lassen, d.h. du musst in der createOrder()-Methode die entsprechende Route des PayPal-Plugins (/store-api/paypal/spb/create-order) aufrufen:

paypal.Buttons({
    // Set up the transaction (server-side)
    createOrder: function(data, actions) {
        return fetch('/store-api/paypal/spb/create-order', {
            method: 'post',
            headers: {
                sw-access-key: accessKey,
                sw-context-token: contextToken
            }
        }).then(function(res) {
            return res.json()
        }).then(function(orderData) {
            return orderData.token
        })
     },
     // etc...
}).render('#paypal-button-container');

Soweit ich weiß, wird anhand des sw-context-token der Warenkorb des Kunden abgerufen und eine Order im Backend erstellt, die mit dieser PayPal-Zahlung dann direkt verknüpft ist (siehe SPBCreateOrderRoute.php).