Payment
Starting with version 6.4.1.0
, Shopware also provides functionality for your app to be able to integrate payment providers. You can choose between just a simple request for approval in the background (synchronous payment) and the customer being forwarded to a provider for payment (asynchronous payment). You provide one or two endpoints, one for starting the payment and providing a redirect URL and one for finalization to check for the resulting status of the payment. The requests and responses of all of your endpoints will be signed and feature JSON content.
Prerequisites
You should be familiar with the concept of Apps, their registration flow as well as signing and verifying requests and responses between Shopware and the App backend server.
Your app server must be also accessible for the Shopware server. You can use a tunneling service like ngrok for development.
Manifest configuration
If your app should provide one or multiple payment methods, you need to define these in your manifest. The created payment methods in Shopware will be identified by the name of your app and the identifier you define per payment method. You should therefore not change the identifier after release, otherwise new payment methods will be created.
You may choose between a synchronous and an asynchronous payment method. These two types are differentiated by defining a finalize-url
or not. If no finalize-url
is defined, the internal Shopware payment handler will default to a synchronous payment. If you do not want or need any communication during the payment process with your app, you can also choose not to provide a pay-url
, then the payment will remain on open on checkout.
Below, you can see different definitions of payment methods.
Depending on the URLs you provide, Shopware knows which kind of payment flow your payment method supports.
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/shopware/trunk/src/Core/Framework/App/Manifest/Schema/manifest-2.0.xsd">
<meta>
<!-- The name of the app should not change. Otherwise, all payment methods are created as duplicates. -->
<name>PaymentApp</name>
<!-- ... -->
</meta>
<payments>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise, a separate method is created. -->
<identifier>asynchronousPayment</identifier>
<name>Asynchronous payment</name>
<name lang="de-DE">Asynchrone Zahlung</name>
<description>This payment method requires forwarding to payment provider.</description>
<description lang="de-DE">Diese Zahlungsmethode erfordert eine Weiterleitung zu einem Zahlungsanbieter.</description>
<pay-url>https://payment.app/async/pay</pay-url>
<finalize-url>https://payment.app/async/finalize</finalize-url>
<!-- This optional path to this icon must be relative to the manifest.xml -->
<icon>Resources/paymentLogo.png</icon>
</payment-method>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise a separate method is created. -->
<identifier>synchronousPayment</identifier>
<name>Synchronous payment</name>
<name lang="de-DE">Synchrone Zahlung</name>
<description>This payment method does everything in one request.</description>
<description lang="de-DE">Diese Zahlungsmethode arbeitet in einem Request.</description>
<!-- This URL is optional for synchronous payments (see below). -->
<pay-url>https://payment.app/sync/process</pay-url>
</payment-method>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise a separate method is created. -->
<identifier>simpleSynchronousPayment</identifier>
<name>Simple Synchronous payment</name>
<name lang="de-DE">Einfache synchrone Zahlung</name>
<description>This payment will not do anything and stay on 'open' after order.</description>
<description lang="de-DE">Diese Zahlungsmethode wird die Transaktion auf 'offen' belassen.</description>
<!-- No URL is provided. -->
</payment-method>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise a separate method is created. -->
<identifier>preparedPayment</identifier>
<name>Payment, that offers everything</name>
<name lang="de-DE">Eine Zahlungsart, die alles kann</name>
<validate-url>https://payment.app/prepared/validate</validate-url>
<capture-url>https://payment.app/prepared/capture</capture-url>
<!-- This optional path to this icon must be relative to the manifest.xml -->
<icon>Resources/paymentLogo.png</icon>
</payment-method>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise a separate method is created. -->
<identifier>refundPayment</identifier>
<name>Refund payments</name>
<name lang="de-DE">Einfache Erstattungen</name>
<refund-url>https://payment.app/refund</refund-url>
<!-- This optional path to this icon must be relative to the manifest.xml -->
<icon>Resources/paymentLogo.png</icon>
</payment-method>
<payment-method>
<!-- The identifier of the payment method should not change. Otherwise a separate method is created. -->
<identifier>recurringPayment</identifier>
<name>Recurring payments</name>
<name lang="de-DE">Einfache wiederkehrende Zahlungen</name>
<recurring-url>https://payment.app/recurring</recurring-url>
<!-- This optional path to this icon must be relative to the manifest.xml -->
<icon>Resources/paymentLogo.png</icon>
</payment-method>
</payments>
</manifest>
Synchronous payments
There are different types of payments. Synchronous payment is the simplest of all and does not need any additional interaction with the customer. If you have defined a pay-url
, you can choose to be informed about and possibly process the payment or not. If you do not need to communicate with your app, you can stop reading here and the transaction will stay open. But if you do define a pay-url
, you can respond to the request with a different transaction status like authorize, paid, or failed. This is useful if you want to add a payment provider that only needs the information if the customer has already provided it in the checkout process or not. For example, a simple credit check for payment upon invoice. Below you can see an example of a simple answer from your app to mark a payment as authorized.
Asynchronous payments
Asynchronous payments are more complicated than synchronous payments. They require interaction with the customer and a redirect to the payment provider, such as PayPal or Stripe.
Here is how it works:
- Shopware sends the first pay
POST
request to start the payment with the payment provider. The request includes all necessary data such as theorder
,orderTransaction
, and areturnUrl
, where the customer should be redirected once the payment process with the payment provider has been finished. - Your app server returns a response with a
redirectUrl
to the payment provider. - The browser will be redirected to this URL and processes his order, and the payment provider will redirect the customer back to the
returnUrl
provided in the first request. - Shopware sends a second
POST
request to thefinalize-url
with theorderTransaction
and all the query parameters passed by the payment provider to Shopware. - Your app server responds with a
status
and amessage
if necessary, like in the synchronous payment.
The second finalize
POST request will be called once the customer has been redirected back to the shop. This second request is only provided with the orderTransaction
for identification purposes and requestData
with all query parameters passed by the payment provider. The response status
value determines the outcome of the payment, e.g.:
Status | Description |
---|---|
cancel | Customer has aborted the payment at the payment provider's site |
fail | Payment has failed (e.g. missing funds) |
paid | Successful immediate payment |
authorize | Delayed payment |
Prepared payments
With Shopware 6.4.9.0
, you can use prepared payments to enhance your checkout process beyond forwarding to a payment provider. This feature enables you to integrate more deeply into the checkout process. This method allows you to prepare the payment before placing the order, e.g., with credit card fields on the checkout confirmation page. Once you add specific parameters to the order placement request in the Storefront, which is also known as the checkout confirmation form, you can pass these parameters to your prepared payment handler. This enables your payment handler to capture the payment successfully when the order is placed.
For this, you have two calls available during the order placement, the validate
call to verify, that the payment reference is valid and if not, stop the placement of the order, and the capture
call, which then allows the payment to be processed to completion after the order has been placed and persisted.
Let's first talk about the validate
call. Here, you will receive three items to validate your payment. The cart
with all its line items, the requestData
from the CartOrderRoute
request and the current salesChannelContext
. This allows you to validate, if the payment reference you may have given your payment handler via the Storefront implementation is valid and will be able to be used to pay the order which is about to be placed. The array data you may send as the preOrderPayment
object in your response will be forwarded to your capture
call, so you don't have to worry about identifying the order by looking at the cart from the validate
call. If the payment is invalid, either return a response with an error response code or provide a message
in your response.
If the payment has been validated and the order has been placed, you then receive another call to your capture
endpoint. You will receive the order
, the orderTransaction
and also the preOrderPayment
array data, that you have sent in your validate call.
WARNING
Keep in mind that if the integration into the checkout process does not work as expected, your customer might not be able to use the prepared payment. This is especially valid for after order payments, since there the order already exists. For these cases, you should still offer a traditional synchronous / asynchronous payment flow. Don't worry, if you have set the transaction state in your capture call to anything but open, the asynchronous payment process will not be started immediately after the prepared payment flow.
Refund
With Shopware 6.4.12.0
, we have also added basic functionality to be able to refund payments. Your app will need to register captured amounts and create and persist a refund beforehand for Shopware to be able to process a refund of a capture.
Similar to the other requests, on your refund
call you will receive the data required to process your refund. This is the order
with all its details and also the refund
which holds the information on the amount
, the referenced capture
and, if provided, a reason
and specific positions
which items are being refunded.
Recurring captures
INFO
Recurring orders and payments require the Subscriptions feature, available exclusively in our paid plans.
Recurring payments are a special case of payment that is used for handling recurring orders, such as subscriptions. The request and response payloads are similar to the synchronous payment flow. At this point, a valid running billing agreement between the customer and the PSP should exist. Use any of the other payment flows to capture the initial order and create such an agreement during the checkout. Afterward, this flow can capture the payment for every recurring payment order.
All possible payment states
The following lists are all possible payment state options:
open
- The payment is open and can be processedpaid
- The payment has been paidcancelled
- The payment has been canceledrefunded
- The payment has been refundedfailed
- The payment has failedauthorize
- The payment has been authorizedunconfirmed
- The payment has not been confirmed yetin_progress
- The payment is in progressreminded
- The payment has been remindedchargeback
- The payment has been charged back
All possible refund states
The following lists are all possible refund state options:
open
- The refund is open and can be processedin_progress
- The refund is in progresscancelled
- The refund has been canceledfailed
- The refund has failedcompleted
- The refund has been refunded
API docs
You can further take a look at Payment references.