Úvod

Po registraci vám přidělíme 2 identifikátory:

Merchant ID

21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5

SecureKey ID (Tajný klíč sloužící k podpisu vašich transakcí)

4RXk9NOYRObb2hPNucUClEHxuYuDm0AKjAYvPLHm9uoGk14WmNOJFVX3bwzoIFVyFxX87MY2WhFTcOo4cRfSFPbpPRtR6Qyh8XkHDXvqVm6KaUaP3vHsjtcPcqz19zWhbjYJyy5VysdclRp6SUnBsz4Gmh8Ufl38NzPNGnSq2cODtWJnWfdkSg0aAd4HpxmV9KJ5tgwtJjL9xPqfboJADf4SxSKbToaUNFsHy93SFiSEno3SO3da8THp4pgKeFuQZm4gRb1sJqkzdgyFqKiAQcTUo6ReVmJZO1dlgAdaohX4i4j5cPK4x5QZRxq21yh73eVjJ6djr5Qhc3W2nJz3gBtVlI1fMRAXF3YoO9haxsOJmBcUYhAlY0OWnqd4oV83ugl022hdb1ACQ9HEryfvYVrVLGcaqkrG2vhvL1XJ0jirZPIbKMhKiHwUW4ETNZFYc8bx8kbjrfJ6JJbdYNkFMmTJDPkqvBGiTL7JGqYR1FjYa4dt1KqMRIeVhaPfbSuqi53cTUc6ZytMmAxxHTx5mVoiimZC30hRxgpYli9WQGcreYFsEfc8wnGT16E6ud6uH2I6uim4WFVQcW7ZyVtbGxpgSBkj79UNfUxHsgRP2NraQK4goyVpb8VJYFIj8xv2QXnqE2bdGx6FqmqDQOqoGb965S18ZAliSFSRprIzO4WhbiwjylSRdCJXmNO8bDpN2UE1eUORWAqQ8pNTNglNglXlwHUyjzj6IEHIt7rL0nHXnG9A7YSAKERT9ynveTWc09xYcWybzpcqVurYhjycx6EP7VYl3OT6QRiRy4fZLbTwSr5ZoizoNYAxD4S9wyf9cVYQrQvNr4WhK9KvsmSEiiWyUX1Bjjl1btScngEteMk87DEzhKQObdHO5A1RrLor7AMnOvegfH3ymopdMqcLBx3cEtrm0edVXBVonNQ1tM1lUfyIEOnUwh45evJDYXeFLtROLzrK2PKBQN

S těmito identifikátory stačí jen sestavit správný request na naši bránu a na tento odkaz v okamžiku placení přesměrovat vašeho uživatele.

Integrace platební brány

Integrace mezi e-shopem a platební bránou probíhá prostřednictvím vygenerování správného URL, na které je přesměrován prohlížeč zákazníka.

O výsledku platby je e-shop informován prostřednictvím přesměrování na URL, kterou sám definuje.

Na straně e-shopu je bezpodmínečně nutné ověřit validnost návratového volání, konkrétně:

  • Ověřit platnost digest pomocí secureKey
  • Ověřit existence orderNumber u daného obchodníka
  • Ověřit správnost stavbu objednávky orderNumber – byla odeslána na platební bránu
  • Ověřit, že částka transakce odpovídá na haléř přesně částce odeslané do platební brány 

Založení platby

Implementační kódy JAVA

import cz.kb.payment.common.dto.TransactionDigestDto;
import cz.kb.payment.common.exception.SystemException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.crypto.codec.Hex;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class DigestExample {
	private static final UUID MERCHANT_ID = UUID.fromString("21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5");
	private static final String MERCHANT_SECURE_KEY = "4RXk9NOYRObb2hPNucUClEHxuYuDm0AKjAYvPLHm9uoGk14WmNOJFVX3bwzoIFVyFxX87MY2WhFTcOo4cRfSFPbpPRtR6Qyh8XkHDXvqVm6KaUaP3vHsjtcPcqz19zWhbjYJyy5VysdclRp6SUnBsz4Gmh8Ufl38NzPNGnSqcODtWJnWfdkSg0aAd4HpxmV9KJ5tgwtJjL9xPqfboJADf4SxSKbToaUNFsHy93SFiSEno3SO3da8THp4pgKeFuQZm4gRb1sJqkzdgyFqKiAQcTUo6ReVmJZO1dlgAdaohX4i4j5cPK4x5QZRxq21yh73eVjJ6djr5Qhc3W2nJ3gBtVlI1fMRAXF3YoO9haxsOJmBcUYhAlY0OWnqd4oV83ugl022hdb1ACQ9HEryfvYVrVLGcaqkrG2vhvL1XJ0jirZPIbKMhKiHwUW4ETNZFYc8bx8kbjrfJ6JJbdYNkFMmTJDPkqvBGiTL7JGqYR1FjYa4dt1KqMRIeVhaPfSuqi53cTUc6ZytMmAxxHTx5mVoiimZC30hRxgpYli9WQGcreYFsEfc8wnGT16E6ud6uH2I6uim4WFVQcW7ZyVtbGxpgSBkj79UNfUxHsgRP2NraQK4goyVpb8VJYFIj8xv2QXnqE2bdGx6FqmqDQOqoGb965S18ZAliSFSRprzO4WhbiwjylSRdCJXmNO8bDpN2UE1eUORWAqQ8pNTNglNglXlwHUyjzj6IEHIt7rL0nHXnG9A7YSAKERT9ynveTWc09xYcWybzpcqVurYhjycx6EP7VYl3OT6QRiRy4fZLbTwSr5ZoizoNYAxD4S9wyf9cVYQrQvNr4WhK9KvmSEiiWyUX1Bjjl1btScngEteMk87DEzhKQObdHO5A1RrLor7AMnOvegfH3ymopdMqcLBx3cEtrm0edVXBVonNQ1tM1lUfyIEOnUwh45evJDYXeFLtROLzrK2PKBQN";
	private static final String CALLBACK_URL = "https://mujobchod.cz/callback";
	private static final String DIGEST_DELIMITER = "|";
	public static void main(String[] args) {
		TransactionDigestDto transactionDigestDto = new TransactionDigestDto();
		transactionDigestDto.setCurrency("CZK");
		transactionDigestDto.setExternalMerchantId(MERCHANT_ID);
		transactionDigestDto.setOrderNumber("13475789");
		transactionDigestDto.setState("MyState");
		transactionDigestDto.setTotalPrice(BigDecimal.valueOf(100L));
		transactionDigestDto.setPaymentProvider("KB");
		transactionDigestDto.setCallbackUrl(CALLBACK_URL);
		final String digest = compute(transactionDigestDto, MERCHANT_SECURE_KEY);
	}

	private static final String HMAC_SHA_256 = "HmacSHA256";
	static String compute(final TransactionDigestDto transaction, final String
	merchantSecureKey) {
		final List < String > values = new ArrayList < >();
		values.add(transaction.getTotalPrice().toString());
		values.add(transaction.getCurrency());
		values.add(transaction.getExternalMerchantId().toString());
		values.add(String.valueOf(transaction.getOrderNumber()));
		values.add(StringUtils.trimToEmpty(transaction.getState()));
		values.add(StringUtils.trimToEmpty(transaction.getPaymentProvider()));
		values.add(StringUtils.trimToEmpty(transaction.getCallbackUrl()));
		final String body = String.join(DIGEST_DELIMITER, values);
		return compute(merchantSecureKey, body);
	}

	private static String compute(final String merchantSecureKey, final String body) {
		try {
			return encode(merchantSecureKey, body);
		} catch(final NoSuchAlgorithmException e) {
			throw new SystemException("Unable to check digest due to NoSuchAlgorithmException", e);
		} catch(final InvalidKeyException e) {
			throw new SystemException("Invalid key exception", e);
		}
	}

	private static String encode(final String key, final String data) throws
	NoSuchAlgorithmException,
	InvalidKeyException {
		final Mac sha256_HMAC = Mac.getInstance(HMAC_SHA_256);
		final SecretKeySpec secretKey = new
		SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), HMAC_SHA_256);
		sha256_HMAC.init(secretKey);
		final byte[] bytes = sha256_HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8));
		return String.valueOf(Hex.encode(bytes));
		org.springframework.security.crypto.codec.Hex;
		org.apache.commons.codec.binary.Hex;
	}
}

Implementační kódy PHP

$merchantId = "21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5";
$secret = "4RXk9NOYRObb2hPNucUClEHxuYuDm0AKjAYvPLHm9uoGk14WmNOJFVX3bwzoIFVyFxX87MY2WhFTcOo4cRfSFPbpPRtR6Qyh8XkHDXvqVm6KaUaP3vHsjtcPcqz19zWhbjYJyy5VysdclRp6SUnBsz4Gmh8Ufl38NzPNGnSq2cDtWJnWfdkSg0aAd4HpxmV9KJ5tgwtJjL9xPqfboJADf4SxSKbToaUNFsHy93SFiSEno3SO3da8THp4pgKeFuQZm4gRb1sJqkzdgyFqKiAQcTUo6ReVmJZO1dlgAdaohX4i4j5cPK4x5QZRxq21yh73eVjJ6djr5Qhc3W2nJz3gBVlI1fMRAXF3YoO9haxsOJmBcUYhAlY0OWnqd4oV83ugl022hdb1ACQ9HEryfvYVrVLGcaqkrG2vhvL1XJ0jirZPIbKMhKiHwUW4ETNZFYc8bx8kbjrfJ6JJbdYNkFMmTJDPkqvBGiTL7JGqYR1FjYa4dt1KqMRIeVhaPfbSuqi5cTUc6ZytMmAxxHTx5mVoiimZC30hRxgpYli9WQGcreYFsEfc8wnGT16E6ud6uH2I6uim4WFVQcW7ZyVtbGxpgSBkj79UNfUxHsgRP2NraQK4goyVpb8VJYFIj8xv2QXnqE2bdGx6FqmqDQOqoGb965S18ZAliSFSRprIzO4WhbijylSRdCJXmNO8bDpN2UE1eUORWAqQ8pNTNglNglXlwHUyjzj6IEHIt7rL0nHXnG9A7YSAKERT9ynveTWc09xYcWybzpcqVurYhjycx6EP7VYl3OT6QRiRy4fZLbTwSr5ZoizoNYAxD4S9wyf9cVYQrQvNr4WhK9KvsmSEiiWyUXBjjl1btScngEteMk87DEzhKQObdHO5A1RrLor7AMnOvegfH3ymopdMqcLBx3cEtrm0edVXBVonNQ1tM1lUfyIEOnUwh45evJDYXeFLtROLzrK2PKBQN";
$callBack = "https://mujobchod.cz/callback";
class TransactionDigestDto {
public $currency = "CZK";
public $ordernumber = "13475789";
public $state = "MyState";
public $totalprice = 100;
public $paymentprovider = "KB";
};
$trn = new TransactionDigestDto();
$digestData = "$trn->totalprice|$trn->currency|$merchantId|$trn->ordernumber|$trn->state|$trn-
>paymentprovider|$callBack";
$digest = hash_hmac('sha256', $digestData, $secret);
$link = "https://pgw.zaplaceno.cz/api/transaction/init?totalPrice=$trn-
>totalprice&currency=$trn->currency&orderNumber=$trn-
>ordernumber&merchantId=$merchantId&digest=$digest&paymentProvider=KB&state=$trn-
>state&callbackUri=$callBack";

Popis rozhraní (request)

Následující atributy jsou zaslány jako parametry v URL při otevírání platební brány:

Parametr Typ Povinnost Vstupuje do výpočtu DIGEST Omezení Popis
totalPrice desetinné číslo ANO ANO Hodnota > 0

Celková cena objednávky (včetně slev, ceny dopravy a balného). Částka se zadává jako desetinné číslo. Desetinným oddělovačem je tečka.

Maximální výše transakce je 99.999 Kč

currency řetězec (string) ANO ANO Formát ISO 4217. Pouze povolené měny. Měna platby dle formátu ISO 4217. Zatím jsou podporovány jen české koruny – CZK.
orderNumber celé číslo ANO ANO Max. délka 10 znaků numerických znaků

Interní číslo objednávky (e-shopu) pro spárování objednávky a platby.

E-shopu přijde při jakékoliv vybrané platební metodě platba s VS obsahujícím OrderNumber.

Číslo musí být pro každého obchodníka jedinečné.

description řetězec (string) NE NE Max. 100 znaků Popis transakce
merchantId řetězec (string) ANO ANO   Jednoznačný Identifikátor obchodníka
email řetězec (string) NE NE   Email plátce. Emailová adresa se nevaliduje. Pokud je nevalidní, nepodaří se zaslat nabídku pojištění CUBIQ.
paymentMethod řetězec (string) NE NE Číselník PaymentMethods Pokud uživatel zvolil metodu již na e-shopu, je zde uvedena (viz číselník níže)
paymentProvider řetězec (string) NE NE Číselník PaymentMethods Pokud uživatel zvolil metodu již na e-shopu, je zde uvedena
logo řetězec (string) NE NE svg Ikona banky pro zobrazení na e-shopu
language řetězec (string) NE NE Může nabývat hodnot dle číselníku (jazyk).

parametru je použita výchozí hodnota z nastavení v e-shop administraci.

Nyní je podporována pouze čeština – CZ.

cubiqInsurance řetězec (string) NE NE Číselník CubiqInsuranceStatus

Atribut identifikuje, zda-li bude zákazníkovi nabídnut produkt CUBIQ pojištění:

  • DISABLED – pojištění se nemá vůbec nabízet (defaultní hodnota)
  • ALLOWED – pojištění se může nabídnout a nebude předvybráno
  • SELECTED – pojištění se může nabídnout a bude předvybráno (např. pojištění uživatel vybral již v e-shopu)

Pokud je allowed nebo selected, musí být vyplněna emailová adresa. Pokud není, je vrácena chyba.

cubiqItems řetězec (string) NE NE  

Kolekce dvojic (CubiqType, Price) zapsaná ve formátu:

phone:5000,tablet:7000,snowboard:12000

CubiqType je číselník

Toto pole je nepovinné i pro CubiqInsurnace = allowed/selected – v tom případě je nabídnuta obecná nabídka.

state řetězec (string) NE ANO Max. 255 znaků

Ochrana proti CSRF nebo identifikátor platby, nepovinný atribut.

S tímto atributem je následně zavolána informace o výsledku platby.

digest řetězec (string) ANO   Délka 64 znaků Elektronický podpis požadavku vypočtený ze všech povinných parametrů a bezpečnostního klíče (viz.validace platebního požadavku)

Číselníky

Číselník PaymentMethods

Hodnota definuje preferovanou platební metodu (nepovinný atribut).

Kód zasílaný e-shopem Význam číselníkové položky
PSD2 Preferovaná platba skrz PSD2
CARD Preferovaná platba skrz platební kartu

Číselník paymentProvider

Hodnota definuje banku, kterou chce uživatel zaplatit

Kód zasílaný e-shopem Význam číselníkové položky
KB Komerční banka
CSAS Česká Spořitelna
AIRBANK AirBank
CSOB Československá obchodní banka

Jak získat seznam povolených bank v e-shopu

Zavolat GET metodu:

https://bo.zaplaceno.cz/api/backoffice/merchant/paymentProviders?merchantId=046ad30a-ed85-4cc9-9803-xxxxxxxxxxx

Výpočet kontrolního součtu (Digest)

Pro zajištění integrity a nepopiratelnosti předávaného požadavku je k ostatním parametrům připojen parametr digest podepisující vybrané atributy z tabulky výše. Platební požadavek bez tohoto parametru, či s neodpovídající hodnotou, je automaticky zamítnut jako neoprávněný.

Hodnota prarametru digest je vypočítána pomocí algoritmu HMAC-SHA256. Vstupem do tohoto algoritmu jsou jednak vybrané klíčové atributy požadavku a též nevěřejný bezpečnostní klíč (secureKey), který zná jen obchodník a platební brána.

Platební brána kontroluje správnost atributu digest oproti zaslaným atributům. Díky tomu lze bezpečně ověřit, že požadavek nebyl ručně změněn a že platbu provádí skutečně daný oprávněný obchodník.

Příklad

Pro platební požadavek je hodnota parametru digest vypočtena následovně:

Parametry platebního požadavku:

totalPrice = 100.00
currency = "CZK"
merchantID = "21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5"
orderNumber = "13475789"
state = None (nevyplněno)

 Příklad bezpečnostního klíče (secureKey):

4RXk9NOYRObb2hPNucUClEHxuYuDm0AKjAYvPLHm9uoGk14WmNOJFVX3bwzoIFVyFxX87MY2WhFTcOo4cRfSFPbpPRtR6Qyh8XkHDXvqVm6KaUaP3vHsjtcPcqz19zWhbjYJyy5VysdclRp6SUnBsz4Gmh8Ufl38NzPNGnSq2cODtWJnWfdkSg0aAd4HpxmV9KJ5tgwtJjL9xPqfboJADf4SxSKbToaUNFsHy93SFiSEno3SO3da8THp4pgKeFuQZm4gRb1sJqkzdgyFqKiAQcTUo6ReVmJZO1dlgAdaohX4i4j5cPK4x5QZRxq21yh73eVjJ6djr5Qhc3W2nJz3gBtVlI1fMRAXF3YoO9haxsOJmBcUYhAlY0OWnqd4oV83ugl022hdb1ACQ9HEryfvYVrVLGcaqkrG2vhvL1XJ0jirZPIbKMhKiHwUW4ETNZFYc8bx8kbjrfJ6JJbdYNkFMmTJDPkqvBGiTL7JGqYR1FjYa4dt1KqMRIeVhaPfbSuqi53cTUc6ZytMmAxxHTx5mVoiimZC30hRxgpYli9WQGcreYFsEfc8wnGT16E6ud6uH2I6uim4WFVQcW7ZyVtbGxpgSBkj79UNfUxHsgRP2NraQK4goyVpb8VJYFIj8xv2QXnqE2bdGx6FqmqDQOqoGb965S18ZAliSFSRprIzO4WhbiwjylSRdCJXmNO8bDpN2UE1eUORWAqQ8pNTNglNglXlwHUyjzj6IEHIt7rL0nHXnG9A7YSAKERT9ynveTWc09xYcWybzpcqVurYhjycx6EP7VYl3OT6QRiRy4fZLbTwSr5ZoizoNYAxD4S9wyf9cVYQrQvNr4WhK9KvsmSEiiWyUX1Bjjl1btScngEteMk87DEzhKQObdHO5A1RrLor7AMnOvegfH3ymopdMqcLBx3cEtrm0edVXBVonNQ1tM1lUfyIEOnUwh45evJDYXeFLtROLzrK2PKBQN

Zřetězení hodnot povinných parametrů probíhá v následujícím pořadí:

a) Bez zvolené banky (paymentProvider): totalPrice + "|" + currency + "|" + merchantId + "|" + orderNumber + "|" +state

a) Se zvolenou bankou (paymentProvider): totalPrice + "|" + currency + "|" + merchantId + "|" + orderNumber + "|" + state + "|" + paymentProvider + "|" + callbackUrl

Příklad zřetězení hodnot parametrů:

100|CZK|21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5|13475789|MyState|KB|https://mujobchod.cz/callback

Výpočet HMAC-SHA256:

Hodnoty oddělené pipou jsou vloženy do atributu data. Klíč je vložen do atributu key.

Hodnota parametru digest pro výše uvedené zřetězené hodnoty parametrů:

1861d3bef67a4b465c78646486bf0256534b8a1b0c2de714a8f0b92b097de038

Zákazník je poté přesměrován na adresu podobnou:

https://pgw.zaplaceno.cz/api/transaction/init?totalPrice=100&currency=CZK&orderNumber=13475789&merchantId=21b9aa87-65c5-4cd0-bc6b-9ae2ac93ebf5&digest=1861d3bef67a4b465c78646486bf0256534b8a1b0c2de714a8f0b92b097de038&paymentProvider=KB&state=MyState&callbackUri=https://mujobchod.cz/callback

Odpověď platební brány

Popis odpovědi do e-shopu (RESPONSE)

Po zpracování platby na straně e-shopu je volána zpět URL s následujícími atributy. URL samotného e-shopu pro callback je nastavena v administraci každého e-shopu.

Parametr Typ Povinný Vstupuje do výpočtu DIGEST Popis
orderNumber celé číslo ANO ANO  
resultCode řetězec (string) ANO ANO Návratový kód, viz číselník ResultCodes
resultDescriptionCz řetězec (string) NE NE Popis návratového stavu, není určeno k business prezentaci uživateli, spíš popis technické chyby
State řetězec (string) NE ANO Zaslána stejná hodnota jako byla v requestu v atributu state
totalPrice desetinné číslo ANO ANO Částka platby, totožná jako request.totalPrice
Digest řetězec (string) ANO   Kontrolní součet

Číselníky

Číselník ResultCodes

Návratový kód z platební brány informující o výsledku platby.

Kód zaslaný platební bránou Význam číselníkové položky
PAID Úspěšně zaplaceno
PENDING Transakce probíhá na straně banky. Obchodník by měl na základě tohoto stavu expedovat zboží. Před jeho samotným zasláním je vhodné počkat na emailovou notifikaci o příchodu finančních prostředků na sběrný účet.
REJECTED Transakce byla zamítnuta
ERROR Nastala chyba během zpracování
TRA_INIT_ERROR Chybný request zaslaný na platební bránu
CANCEL_BY_USER Operace byla zrušena uživatelem

Výpočet kontrolního součtu (Digest)

E-shop musí provést kontrolu validnosti zaslaných atributů. Výpočet digest probíhá obdobným způsobem jako u requestu:

Kontrolní součet je spočítán jako SHA256 následujících atributů (pozor – merchantNumber nepřichází v requestu, zná ho spolu se secureKey jen obchodník a platební brána):

orderNumber + "|" + resultCode + "|" + state + "|" + totalPrice + "|" + merchantId

Příklad zřetězení hodnot parametrů:

5956242|OK||1.00|4c534de4-cb7b-4d53-b128-f5edaa47a9e4

Výpočet HMAC-SHA256:

Hodnoty oddělené pipou jsou vloženy do atributu data. Klíč je vložen do atributu key.

hash_hmac('sha256', '5956242|OK||1.00|4c534de4-cb7b-4d53-b128-f5edaa47a9e4', '2mzLcBYuXAfjEMDVTjcjhgEco7lz8k2D0….4e5I0oID3')

Hodnota parametru digest pro výše uvedené hodnoty parametrů:

fc9418ac1e50565dc8c9f7b31a6eab4d0fc2cdd7a41afbd93b785423758e0a05

Příklad validního callback URL:

http://muje-shop.cz/?orderNumber=5956242&resultCode=PAID&resultDescriptionCz=OK&totalPrice=1.00&digest=fc9418ac1e50565dc8c9f7b31a6eab4d0fc2cdd7a41afbd93b785423758e0a05

Na straně e-shopu je bezpodmínečně nutné ověřit validnost návratového volání, konkrétně:

  • Ověřit platnost digest pomocí secureKey
  • Ověřit existence orderNumber u daného obchodníka
  • Ověřit správnost stavbu objednávky orderNumber – byla odeslána na platební bránu
  • Ověřit, že částka transakce odpovídá na haléř přesně částce odeslané do platební brány