Push notifikace
Předání výsledku platby na pozadí
Push notifikace slouží k předání výsledku platby na pozadí z platební brány Comgate na server obchodníka. Tato metoda využívá HTTP požadavek (webhook) na definovanou adresu (endpoint), který upozorní na změnu stavu platby, například při úspěšném dokončení, autorizaci, expiraci nebo zrušení platby.
Bezpečné ověření stavu platby
Skutečný stav platby je doporučeno vždy ověřit prostřednictvím volání API metody /status. Toto ověření zaručuje spolehlivost a bezpečnost, protože informace přijaté push notifikací nemusí vždy přesně odrážet finální stav platby.
Nastavení endpointu pro push notifikace
Adresu, na kterou mají být push notifikace zasílány, lze nastavit v klientském portále. Doporučuje se nastavit adresu, která podporuje HTTPS, aby byla zajištěna bezpečnost komunikace.
Parametry push notifikace
Výsledek platby je předáván jako HTTP POST požadavek s následujícími parametry:
Parametr | Typ | Povinný | Popis |
---|---|---|---|
transId | string | A | Unikátní ID transakce |
merchant | string | A | Identifikátor e-shopu v systému Comgate |
test | boolean | A | true pro testovací platbu, false pro produkční |
price | integer | A | Cena za produkt v haléřích nebo centech |
curr | string | A | Kód měny dle ISO 4217 |
label | string | A | Krátký popis produktu (1-16 znaků) |
refId | string | A | Reference platby (např. variabilní symbol nebo číslo objednávky) |
payerId | string | N | Identifikátor plátce v systému e-shopu |
payerName | string | N | Jméno účtu plátce |
payerAcc | string | N | Číslo účtu plátce |
method | string | N | Použitá metoda platby |
account | string | N | Identifikátor bankovního účtu e-shopu |
email | string | A | Kontaktní e-mail plátce |
phone | string | N | Kontaktní telefon plátce |
name | string | N | Identifikátor produktu pro vyhledávání ve statistikách |
secret | string | A | Bezpečnostní klíč pro komunikaci |
status | string | A | Aktuální stav transakce (PAID , CANCELLED , AUTHORIZED ) |
fee | string | N | Poplatek za transakci (pokud je nastaven) |
fullName | string | A | Jméno a příjmení plátce |
billingAddrCity | string | N | Fakturační adresa - město |
billingAddrStreet | string | N | Fakturační adresa - ulice |
billingAddrPostalCode | string | N | Fakturační adresa - PSČ |
billingAddrCountry | string | N | Fakturační adresa - země (ISO 3166 alpha-2) |
delivery | string | N | Způsob doručení (HOME_DELIVERY , PICKUP , ELECTRONIC_DELIVERY ) |
homeDeliveryCity | string | N | Doručovací adresa - město |
homeDeliveryStreet | string | N | Doručovací adresa - ulice |
homeDeliveryPostalCode | string | N | Doručovací adresa - PSČ |
homeDeliveryCountry | string | N | Doručovací adresa - země (ISO 3166 alpha-2) |
category | string | N | Kategorie produktu (PHYSICAL_GOODS_ONLY , OTHER ) |
Výsledky předané přes notifikaci nebo přesměrováním v URL se nedoporučuje považovat za plně důvěryhodné, jelikož tyto metody mohou být náchylné k manipulaci. Skutečný stav platby je doporučeno vždy ověřit prostřednictvím volání API metody /status společně s obdrženým transId.
Parametry odpovědí
Parametr | Typ | povinný | Popis |
---|---|---|---|
code | integer | A | Návratový kód metody a popis chyby: systém očekává HTTP kód 200, v případě, že výsledek platby byl v pořádku přijat.. |
Push notifikace očekává potvrzení přijetí formou HTTP kódu 200. V opačném případě je notifikace opakovaně zasílána (až 1000 pokusů). Pokud i po opakovaných pokusech nedojde k úspěšnému předání, je vygenerována chybová zpráva a odeslána na kontaktní e-mail obchodníka.
Tip: Pokud přijetí notifikace není potvrzeno, ujistěte se, že endpoint vrací kód 200. Také můžete nastavit e-mail pro zasílání chyb v sekci Integrace v klientském portále, kde můžete definovat více e-mailů oddělených středníkem.
Příklad předání výsledku platby na pozadí – HTTP request pomocí cURL
curl -X POST -i --data "merchant=merchant_com&test=false&price=10000&curr=CZK&label=Beatles%20-%20Help&refId=2010102600&method=CARD&email=info%40customer.com&phone=%2B420123456789&transId=AB12-EF34-IJ56&secret=ZXhhbXBsZS5jb206QUJDeHl6&status=PAID" https://example.com/handler.php
Příklad předání výsledku platby na pozadí – HTTP response
HTTP/2 200 OK
content-type: application/x-www-form-urlencoded; charset=UTF-8
Komunikace mezi systémem Klienta a serverem platební brány je zabezpečena pomocí hesla a IP whitelistu. Přístup musí být povolen pouze z IP adresy serveru platební brány. Rozsahy IP adres jsou definovány v sekci Zabezpečení. Je povinné použít protokol HTTPS, který znemožňuje prozrazení hesla při případném odposlouchávání komunikace. Heslo je předáváno jako POST parametr (nikoliv GET parametr) proto, aby se neukládalo v logu komunikace webového serveru.
E-shop na svojí straně zajistí, že zboží (služba) poskytnuté v rámci zaplacené transakce (identifikované pomocí unikátního transaction ID) bude vydáno Plátci pouze jednou (i při opakovaném předání výsledku stejné platby na server Klienta).
Implementace a ukázka kódu
Ukázka endpointu pro příjem push notifikací
- PHP
- Java
- Python
- C#
<?php
// Webhook endpoint for push notification
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Load data from push notification using POST (x-www-form-urlencoded) or REST (JSON)
// POST method (x-www-form-urlencoded)
parse_str(file_get_contents('php://input'), $data);
// REST method (JSON)
// $data = json_decode(file_get_contents('php://input'), true);
if (isset($data['transId'])) {
$paymentId = $data['transId'];
// Prepare headers and parameters for payment verification
$headers = [
'Content-Type: application/x-www-form-urlencoded',
'Accept: application/x-www-form-urlencoded',
];
$request_params = [
'merchant' => '123456',
'transId' => $paymentId,
'secret' => 'gx4q8OV3TJt6noJnfhjqJKyX3Z6Ych0y',
];
// Initialize cURL for API call
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://payments.comgate.cz/v1.0/status');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($request_params));
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
try {
// Execute cURL request
$response = curl_exec($ch);
if ($response === false) {
throw new Exception(curl_error($ch), curl_errno($ch));
}
// Output API response for verification
print_r($response);
} catch (Exception $e) {
print_r($e->getMessage());
} finally {
curl_close($ch);
}
// Respond to push notification
http_response_code(200);
echo "OK";
} else {
http_response_code(400);
echo "Bad Request";
}
}
?>
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.json.JSONObject;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.*;
@RestController
@RequestMapping("/webhook")
public class WebhookController {
@PostMapping
public ResponseEntity<String> handlePushNotification(@RequestBody String payload) {
try {
// Load data from push notification using POST (x-www-form-urlencoded) or REST (JSON)
JSONObject data = new JSONObject(payload);
String transId = data.optString("transId", null);
if (transId != null) {
// Prepare the API URL and connection
URL url = new URL("https://payments.comgate.cz/v1.0/status");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("Accept", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
// Prepare the request parameters
String params = "merchant=123456&transId=" + transId + "&secret=gx4q8OV3TJt6noJnfhjqJKyX3Z6Ych0y";
// Send the request
OutputStream os = conn.getOutputStream();
os.write(params.getBytes());
os.flush();
os.close();
// Read the response
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
conn.disconnect();
// Return success response
return ResponseEntity.ok("OK");
} else {
return ResponseEntity.badRequest().body("Bad Request: transId missing");
}
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.status(500).body("Internal Server Error");
}
}
}
from flask import Flask, request, jsonify
import requests
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
# Load data from push notification using POST (x-www-form-urlencoded) or REST (JSON)
# POST method (x-www-form-urlencoded)
data = request.form.to_dict()
# REST method (JSON)
# data = request.get_json()
if 'transId' in data:
payment_id = data['transId']
# Prepare headers and payload for API call
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/x-www-form-urlencoded'
}
payload = {
'merchant': '123456',
'transId': payment_id,
'secret': 'gx4q8OV3TJt6noJnfhjqJKyX3Z6Ych0y'
}
# Send POST request to API
try:
response = requests.post('https://payments.comgate.cz/v1.0/status', headers=headers, data=payload)
response.raise_for_status()
# Return success response to the webhook
return "OK", 200
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
return "Internal Server Error", 500
else:
return "Bad Request: transId missing", 400
if __name__ == '__main__':
app.run(port=5000)
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
[ApiController]
[Route("webhook")]
public class WebhookController : ControllerBase
{
[HttpPost]
public async Task<IActionResult> HandlePushNotification([FromBody] Dictionary<string, string> data)
{
// Check if transId is in the data
if (data.TryGetValue("transId", out var transId))
{
using (var client = new HttpClient())
{
// Prepare request parameters
var values = new Dictionary<string, string>
{
{ "merchant", "123456" },
{ "transId", transId },
{ "secret", "gx4q8OV3TJt6noJnfhjqJKyX3Z6Ych0y" }
};
var content = new FormUrlEncodedContent(values);
// Send POST request to API
var response = await client.PostAsync("https://payments.comgate.cz/v1.0/status", content);
// Check if the response was successful
if (response.IsSuccessStatusCode)
{
// Return success response to the webhook
return Ok("OK");
}
else
{
return StatusCode((int)response.StatusCode, "API call failed");
}
}
}
else
{
return BadRequest("transId missing");
}
}
}