Grafisches Interface planen und umsetzen
Welche Transaktionsinformationen stehen schon fest und welche soll der Kunde selbst bestimmen? Zum Beispiel werden die Artikel (Line Items) sowie die Adresse durch den Kunden bestimmt und die Währung durch den Merchant.
Das würde dann bedeuten, dass es in der (Web) Applikation Input Felder gibt, welche der Kunde ausfüllt (z.B. für die Adresse) und andere Werte wie die Währung bereits im Code definiert wird.
Diese Werte werden dann z.B. in PHP mit $_POST["street"] in den SDK Code geladen, nachdem das HTML-Formular abgesendet wurde. Das entsprechende HTML-Input Feld hätte dann in diesem Beispiel das Attribut name="street".
Transaktion vorbereiten
Damit für den Kunden die möglichen Zahlungsarten angezeigt werden, muss dafür bereits eine offene Transaktion im Portal erstellt werden. Auf der Grundlage dieser Transaktion werden dann die möglichen Zahlungsarten geladen.
Beispielsweise, wenn die vorbereitete Transaktion die Währung Euro hat, wird die Zahlungsart TWINT nicht geladen. Oder bei einer Rechnungszahlart mit Bonitätsprüfung, wird diese nicht geladen, wenn die Adresse ungültig oder die Bonität negativ ist.
Line Items
Erstellte Line Items, welche der Transaktion angefügt wurden, sind im Portal ersichtlich:
Artikel (Line Item) erstellen
Das sind die einzelnen Produkten, welche auf einer Transaktion vorhanden sind.
$lineItem = new LineItemCreate(); $lineItem->setName('Red T-Shirt'); $lineItem->setUniqueId('5412'); $lineItem->setSku('red-t-shirt-123'); $lineItem->setQuantity(1); $lineItem->setAmountIncludingTax(10); $lineItem->setType(LineItemType::PRODUCT); |
Mehrwertsteuern für Artikel definieren
Die Mehrwertsteuern werden pro Artikel definiert. Der angegebene Prozentsatz der Mehrwertsteuer erhöht jedoch den Preis des Artikels nicht mehr, sondern gilt als inklusive.
Kostet ein Artikel 10 Euro und der Steuersatz ist 10%, so kostet der Artikel an sich 9 Euro und die ca. 1 Euro gelten als Steuern.
$tax = new TaxCreate(); $tax->setTitle("Mehrwertsteuern"); $tax->setRate(10); $lineItem->setTaxes($tax); |
Attribute für Artikel erstellen
Die Attribute sind Eigenschaften eines Artikels, welche aus einem Label (Titel des Attributs) sowie einem Wert (z.B. einer Eigenschaft) besteht. Diese Werte können frei bestimmt werden.
$attribute = new LineItemAttributeCreate(); $attribute->setLabel("ATTRIBUTE"); $attribute->setValue("just a line item attribute"); $attributes = []; $attributes["code"] = $attribute; $lineItem->setAttributes($attributes); |
Coupon (Discount) setzen
Ein Coupon wird nicht direkt vom Betrag eines Artikels abgezogen, sondern wird als separater Artikel definiert, welcher dann vom Totalbetrag abgezogen wird:
Hierfür muss auch ein negativer Betrag angegeben werden.
$lineItemDiscount = new LineItemCreate(); $lineItemDiscount->setType(LineItemType::DISCOUNT); $lineItemDiscount->setUniqueId("discount-" . rand(55, 4)); $minusAmount = -40; $lineItemDiscount->setAmountIncludingTax($minusAmount); $lineItemDiscount->setSku('discount'); $lineItemDiscount->setName('Discount for order'); $lineItemDiscount->setQuantity(1); |
Produktgebühr angeben
Für den Erwerb eines Produkts kann neben dem Produktpreis auch noch eine Gebühr erhoben werden.
Dies stellt wie beim Coupon ein separater Artikel dar, der in diesem Fall zum Totalbetrag dazu addiert wird.
$lineItemFee = new LineItemCreate(); $lineItemFee->setType(LineItemType::FEE); $lineItemFee->setUniqueId("fee-" . rand(55, 4)); $lineItemFee->setAmountIncludingTax(10); $lineItemFee->setSku('order_fee'); $lineItemFee->setName('Fee for order'); $lineItemFee->setQuantity(1); |
Versandkosten angeben
Auch als separaten Artikel gelten mögliche Versandkosten, welche wie folgt definiert werden:
$shippingItem = new LineItemCreate(); $shippingItem->setSku('shipping'); $shippingItem->setUniqueId('shipping-' . rand(5, 50)); $shippingItem->setType(LineItemType::SHIPPING); $shippingItem->setName('Shipping'); $shippingItem->setQuantity(1); $shippingItem->setAmountIncludingTax(5); |
Artikel, Coupons und Gebühr der Transaktion hinzufügen
Nun müssen die definierten Artikel, Coupons, Versandkosten und Gebühren noch der Transaktion zugeordnet werden, bevor diese im Portal angelegt werden kann:
$transactionPayload = new TransactionCreate(); $transactionPayload->setLineItems([$lineItems, $lineItemDiscount, $lineItemFee, $shippingItem]); |
Dem $transactionPayload (TransactionCreate())Objekt werden alle Transaktionsdaten hinzugefügt wie auch die Adresse, Success/Failed URL, Ausführungsmodus (Direkte/Verzögerte Verbuchung) und weitere Daten.
Adressen
Die Adresse ist direkt auf einer Transaktion im Portal ersichtlich.
Das Hinzufügen von Adressen geht wie folgt.
$billingAddress = new AddressCreate( [ "city" => "Winterthur", "country" => "CH", // required "email_address" => "test@mail.com", "family_name" => "Customer", "given_name" => "Good", "postcode" => "8400", "street" => "Teststrasse" ] ); $transactionPayload->setBillingAddress($billingAddress); $transactionPayload->setShippingAddress($billingAddress); |
Die Versands- und Rechnungsadressen können auch voneinander abweichen.
Adressen können teils optional sein oder auch obligatorisch bei Bonitätsprüfungen oder zum Beispiel bei Länderfilter, wenn eine Zahlungsart für ein Land nicht zur Verfügung steht.
Weitere wichtige Attribute hinzufügen
Merchant Reference
In der Transaktionsübersicht im Listenmenü im Portal gibt es die Merchant Reference.
Mit dieser kann ebenfalls die Transaktion Identifiziert werden (sofern diese Merchant Reference eindeutig definiert wurde) oder wenn z.B. mehrere Shops an denselben Portal Space gekoppelt sind, können die Bestellungen von verschiedenen Shops besser auseinander gehalten werden.
Ausführungsmodus
Man kann bestimmen, ob die Transaktion sofort verbucht werden soll:
$transactionPayload->setCompletionBehavior(TransactionCompletionBehavior::COMPLETE_IMMEDIATELY) |
Oder ob der Betrag vorerst nur reserviert werden soll und der Händler dann manuell die Transaktion verbuchen muss:
$transactionPayload->setCompletionBehavior(TransactionCompletionBehavior::COMPLETE_DEFERRED); |
Metadaten hinzufügen
Es können noch weitere transaktions-unabhängige Werte dem Transaktionsobjekt hinzugefügt werden:
$metaData = array("INDEX" => "a new test"); $transactionPayload->setMetaData($metaData); |
Diese Werte bestehen dann auf der Transaktion und können zu jedem späteren Zeitpunkt abgerufen werden, jedoch auch bereits auf der Payment Page:
Im entsprechenden TWIG File in diesem Fall den Wert von INDEX auslesen:
{{ transaction.metaData.INDEX }}
Transaktion ans Portal senden
Transaktion erstellen
Nachdem all die gewünschten Daten dem Transaktion Objekt ($transactionPayload) hinzugefügt wurden, kann die Transaktion erstellt werden:
$client = new ApiClient($userAppId, $token) $transaction = $client->getTransactionService()->create($spaceId, $transactionPayload); |
Dazu benötigt man das ApiClient() Objekt, welche die Autorisierung und die Interaktionen mit dem Portal übernimmt. Darauf kann über den Transaktion Service die Transaktion im Portal angelegt werden.
Die User App ID sowie den Token findest Du im Applikationsuser Menü.
Transaktion updaten
Gewisse Daten, wie die Transaktion ID, stehen natürlich erst zur Verfügung, nachdem die Transaktion im Portal angelegt wurde. Zum Beispiel für die Weiterleitungslinks nach der Transaktion, kann es sinnvoll sein, die Transaktion ID mitzugeben.
Success/Failed URLs
Was soll passieren, wenn die Zahlung erfolgreich oder fehlgeschlagen ist?
Hierfür können auf der Transaktion Success und Failed URL’s definiert werden, wo man auf die jeweils gewünschte Seite weitergeleitet wird, je nach Resultat der Transaktion:
$transactionPending = new TransactionPending([ "id" => $transaction->getId(), "version" => 1 ]); $transactionPending->setSuccessUrl("https://shop.com/order/success&txId=" . $transaction->getId()); $transactionPending->setFailedUrl("https://shop.com/order/failed&txId=" . $transaction->getId()); $client->getTransactionService()->update($spaceId, $transactionPending); |
Da die Transaktion zuvor schon angelegt wurde, wird das Objekt TransactionPending benötigt um die Success/Failed URL’s der bestehenden Transaktion anzufügen, bzw. diese zu updaten (mit update()).
Zahlungsarten auslesen
Mit der angelegten Transaktion wurde die Grundlage geschaffen, die Zahlungsarten zu laden.
Je nach den Transaktionsdaten (Währung, Adresse) können möglicherweise regionale Zahlungsarten oder welche, die nur mit positiver Bonität funktionieren nicht geladen.
Weiter muss bei den Zahlungsarten unterschieden werden, ob es sich um eine On-Site oder Off-Site Zahlungsart handelt.
Dies steht jeweils auf den betroffenen Zahlungsart im Portal.
Off-site Zahlungsarten, wie TWINT oder PF Card, leiten den Kunden auf eine externe Zahlungsseite weiter, wo der Kunde seine Zahlungsangaben macht.
On-site Zahlungsarten wie Kreditkarten werden auf dem Shop integriert, entweder als Lightbox Integration (Pop-up Box wo die Zahlungsangaben gemacht werden) oder als Iframe (direkt auf der Website-Fluss integriert).
Zum Laden der Zahlungsarten muss also ein Integrationsmodus gewählt werden:
“payment_page”, “iframe” oder “lightbox”
Mit einem Foreach-Loop können dann alle Zahlungsarten ausgelesen werden.
$paymentMethods = $client->getTransactionService()->fetchPaymentMethods($spaceId, $transaction->getId(), “iframe”); foreach ($paymentMethods as $paymentMethod) { echo $paymentMethod->getResolvedImageUrl(); echo $paymentMethod->getId(); echo $paymentMethod->getName(); } |
Am wichtigsten ist hier die ID der Zahlungsmethode, welche den Integrationen (Payment Page, Iframe oder Lightbox) mitgegeben werden muss, damit die korrekten Zahlungsformulare für die ausgewählte Zahlungsmethode geladen werden.
Mit der Image-URL kann das Bild der Zahlungsmethode über das <img> HTML Tag geladen werden, sowie kann der Name der Zahlungsmethode dargestellt werden.
Zahlungsintegrationen realisieren (Bezahlen)
Sobald eine Zahlungsmethode ausgewählt wurde und dessen ID dem Shop bekannt ist, kann eine Zahlungsintegration implementiert werden. Die Zahlungsintegration (Payment Page, Iframe, Lightbox) ist jene, welche zuvor als Parameter beim Laden der Zahlungsmethoden angegeben wurde.
Eine Gateway für die jeweiligen Integrationen könnte in etwa so aussehen:
public function proceedPayment($transactionId, $paymentMethodConfigurationId) { switch ($this->integrationMode) { case "iframe": $this->proceedViaIframe($transactionId, $paymentMethodConfigurationId); break; case "lightbox": $this->proceedViaLightbox($transactionId, $paymentMethodConfigurationId); break; default: $this->proceedViaPaymentPage($transactionId, $paymentMethodConfigurationId); break; } } |
Die benötigten Daten sind hier:
- die Transaktion ID, damit die richtige Transaktion zur Bezahlung verwendet wird
- die Zahlungsmethoden ID, damit die richtigen Zahlungsformulare geladen werden
- den Integrationsmodus, womit die konkrete Umsetzung des jeweiligen Integrationsmodus ausgewählt werden kann
In den folgenden Unterkapiteln die jeweiligen Integrationen.
Payment Page
Als Erstes wird die Transaktion auf die ausgewählte Zahlungsmethode beschränkt:
private function proceedViaPaymentPage($transactionId, $paymentMethodConfigurationId) { $transaction = $client->getTransactionService()->read($spaceId, $transactionId); $transactionPending = new TransactionPending([ "id" => $transactionId, "version" => $transaction->getVersion() + 1 ]); $transactionPending->setAllowedPaymentMethodConfigurations($paymentMethodConfigurationId); $client->getTransactionService()->update($spaceId, $transactionPending); |
Im zweiten Schritt wird basierend auf dieser Transaktion ein Link für die Payment Page generiert und via JavaScript wird auf Knopfdruck, dann auf diese Seite weitergeleitet:
$redirectionUrl = $client->getTransactionPaymentPageService()->paymentPageUrl($spaceId, $transactionId);
echo "Payment Page URL: <a href='" . $redirectionUrl . "'>$redirectionUrl</a>"; echo "<br> <script src=\"https://code.jquery.com/jquery-3.6.0.min.js\"></script> <div class='button' id=\"pay-button\">Pay (via payment page)</div> <script> $('#pay-button').on('click', function(){ window.location.replace('$redirectionUrl'); }); </script>"; } |
Auf der Zahlungsseite ist dann ausschliesslich jene ausgewählte Zahlungsmethode zu sehen.
Nach erfolgreicher oder erfolgloser Transaktion wird man auch die jeweils definierten Success/Failed URL’s weitergeleitet.
Iframe
Die Iframe Logik wird vom Portal bereitgestellt. Dies über ein JavaScript File, welche mithilfe des Space und der betroffenen Transaktion geladen werden kann:
try { $javaScriptUrl = $client->getTransactionIframeService()->javascriptUrl($spaceId, $transactionId); } catch (Exception $e) { // Error message die; } |
In einem zweiten Schritt wird die geladene JavaScript-File URL implementiert, zusammen mit einem Zahlungsbutton und weiteren Abhängigkeiten:
echo “<ul id=\"payment-errors\"></ul> <div id=\"payment-form\"></div> <button class='button is-primary' id=\"pay-button\">Pay</button> <script src=\"https://code.jquery.com/jquery-3.6.0.min.js\"></script> <script src=\"$javaScriptUrl\" type=\"text/javascript\"></script>”; |
Basierend auf dem geladenen JavaScript muss jetzt noch die Zahlungsbestätigung implementiert werden. Das geladene Zahlungsformular wird hiermit über Ajax abgesendet:
echo “<script type=\"text/javascript\">
// Set here the id of the payment method configuration the customer chose. var paymentMethodConfigurationId = $paymentMethodConfigurationId;
// Set here the id of the HTML element the payment iframe should be appended to. var containerId = \"payment-form\";
var handler = window.IframeCheckoutHandler(paymentMethodConfigurationId);
handler.setValidationCallback( function(validationResult){ // Reset payment errors $(\"#payment-errors\").html(\"\");
if (validationResult.success) { // Create the order within the shop and eventually update the transaction. $.ajax(\"https://$_SERVER[HTTP_HOST]/order\", { success: function(){ handler.submit(); } }); } else { // Display errors to customer $.each(validationResult.errors, function(index, errorMessage){ $(\"#payment-errors\").append(\"<li>\" + errorMessage + \"</li>\"); }); } });
//Set the optional initialize callback handler.setInitializeCallback(function(){ //Execute initialize code });
//Set the optional height change callback handler.setHeightChangeCallback(function(height){ //Execute code });
handler.create(containerId)
$(\"#pay-button\").on(\"click\", function(){ handler.validate(); }); </script>"; |
Hier muss auch die konkrete Zahlungsmethoden ID angegeben werden, dass das korrekte Iframe zur Zahlungsmethode geladen wird.
Bei der $.ajax() Methode muss die URL noch so angepasst werden, damit es der URL entspricht, wo das Iframe hineingeladen wird.
Nach erfolgreicher oder erfolgloser Transaktion wird man auch die jeweils definierten Success/Failed URL’s weitergeleitet.
Lightbox
Die Lightbox Integration ist ähnlich aufgebaut wie beim Iframe.
Zuerst wird basierend auf der Transaktion wieder eine URL zum Lightbox spezifischen JavaScript-File geladen:
private function proceedViaLightbox($transactionId, $paymentMethodConfigurationId) { try { $javaScriptUrl = $client->getTransactionLightboxService()->javascriptUrl($spaceId, $transactionId); } catch (Exception $e) { // Error message die; } |
In einem zweiten Schritt wird die geladene JavaScript-File URL implementiert, zusammen mit einem Zahlungsbutton und weiteren Abhängigkeiten:
echo "<br> <button class='button' id=\"pay-button\">Pay (open lightbox)</button>
<script src=\"https://code.jquery.com/jquery-3.6.0.min.js\"></script> <script src=\"$javaScriptUrl\" type=\"text/javascript\"></script>"; |
Basierend auf dem geladenen JavaScript muss jetzt noch die Zahlungsbestätigung implementiert werden:
echo "<script type=\"text/javascript\">
// Set here the id of the payment method configuration the customer chose. var paymentMethodConfigurationId = $paymentMethodConfigurationId;
$('#pay-button').on('click', function(){ window.LightboxCheckoutHandler.startPayment(paymentMethodConfigurationId, function(){ alert('An error occurred during the initialization of the payment lightbox.'); }); }); </script>"; } |
Hier muss auch die konkrete Zahlungsmethoden ID angegeben werden, damit die korrekte Lightbox zur Zahlungsmethode geladen wird.
Nach erfolgreicher oder erfolgloser Transaktion wird man auch die jeweils definierten Success/Failed URLs weitergeleitet.
Transaktion Updates vom Portal erhalten (Webhooks)
Nachdem nun via Payment Page, Iframe oder Lightbox durch den Kunden bezahlt wurde, wird im Portal der Zahlungsstatus geändert.
Solche Updates können auch an den Shop übertragen werden.
Dies passiert durch das Portal automatisch, sofern man die richtigen Webhooks erstellt hat.
Webhooks einrichten
Dazu muss zuerst eine Webhook URL im Portal erstellt werden. Dies ist eine spezifische URL auf dem Shop, wo ein Skript dahinter jegliche Requests empfängt und jene gesendeten Webhooks abspeichert.
Folgende IP-Adressen müssen hierfür auf dem Shop Server ge-whitelistet sein: https://app-wallee.com/en-us/doc/webhooks#Public%20IP%20Addresses
Anschliessen muss ein Webhook Listener erstellt werden, verknüpft mit der erstellten Webhook URL:
In diesem Beispiel (siehe Bild) wird beim Update des Transaktion Status auf “Fulfill”, “Failed”, “Authorized” und “Voided” jeweils ein JSON-Request an die angegebene URL gesendet.
Das JSON sieht in etwa so aus:
{ "eventId": 138833842, "entityId": 63762876, // transaction id "listenerEntityId": 1472041829003, "listenerEntityTechnicalName": "Transaction", "spaceId": 30140, "webhookListenerId": 285874, "timestamp": "2022-08-23T14:20:53+0000" } |
Webhooks im Shop empfangen
Im Skript, welches hinter der angegebener URL liegt, müssen nun diese JSON-Requests entgegengenommen und gespeichert werden.
In PHP kann dies in etwa so aussehen:
$input = file_get_contents('php://input'); $content = json_decode($input); if ($content != null) { $webhook = new WebhookRequest($content->eventId, $content->entityId, $content->listenerEntityId, $content->listenerEntityTechnicalName, $content->spaceId, $content->webhookListenerId, $content->timestamp); file_put_contents(__DIR__ . '/webhook.log', print_r(array($webhook), true) . "\n", FILE_APPEND); } |
Mit file_get_contents('php://input')werden eingehende Requests empfangen und mit json_decode($input)können die JSON-Eigenschaften ausgelesen werden.
In diesem Beispiel werden diese Attribute in einem File (webhook.log) abgespeichert.
Anbei noch die Klasse WebhookRequest, welche hier als Datenstruktur verwendet wurde:
class WebhookRequest { private $eventId; private $entityId; private $listenerEntityId; private $listenerEntityTechnicalName; private $spaceId; private $webhookListenerId; private $timestamp; public function __construct($eventId, $entityId, $listenerEntityId, $listenerEntityTechnicalName, $spaceId, $webhookListenerId, $timestamp) { $this->eventId = $eventId; $this->entityId = $entityId; $this->listenerEntityId = $listenerEntityId; $this->listenerEntityTechnicalName = $listenerEntityTechnicalName; $this->spaceId = $spaceId; $this->webhookListenerId = $webhookListenerId; $this->timestamp = $timestamp; } public static function receive($webhook) { var_dump($webhook); } public function getEventId() { return $this->eventId; } public function getEntityId() { return $this->entityId; } public function getListenerEntityId() { return $this->listenerEntityId; } public function getListenerEntityTechnicalName() { return $this->listenerEntityTechnicalName; } public function getSpaceId() { return $this->spaceId; } public function getWebhookListenerId() { return $this->webhookListenerId; } public function getTimestamp() { return $this->timestamp; } } |
Empfangene Webhooks auswerten (Transaktion Status bekommen)
Beim empfangenen Transaktion-Webhook (Transaktion Update) haben die gespeicherten Webhook Attribute folgende Bedeutung: https://app-wallee.com/en-us/doc/webhooks#_integration_details
Um den Transaktion Status Update nun zu bekommen, muss die Entity ID ausgelesen werden, welche der Transaktion ID entspricht.
Jetzt muss über den Transaktion Service die Transaktion mit der ID ausgelesen werden, um den aktuellen Transaktion Status zu bekommen:
$transaction = $client->getTransactionService()->read($spaceId, $transactionId) echo $transaction->getState(); |
Damit kann dieser Wert jetzt z.B. in der Shop Datenbank in der betroffenen Transaktion Entität gespeichert werden.
Kommentare
0 Kommentare
Bitte melden Sie sich an, um einen Kommentar zu hinterlassen.