R
Depending on the use of Transbank's REST or SOAP version methods change, but the idea is the same. In the examples I will use the configuration of the integration environmentStep 1: Create TransactionI'm gonna do a couple of functions to get me a transaction ready for you to send the client to trasbank: https://www.transbankdevelopers.cl/referencia/webpay#crear-una-transaccion-webpay-plus :use Transbank\Webpay\WebpayPlus\Transaction;
use Transbank\Webpay\Options;
function createRestTransaction($buyOrder, $amount) {
$config = Options::defaultConfig();
return Transaction::create(
$buyOrder,
session_id(),
$amount,
TU_URL_FINAL,
$config
);
} https://www.transbankdevelopers.cl/referencia/webpay-soap?l=php#crear-una-transaccion-webpay-plus-normal :use Transbank\Webpay\Webpay;
use Transbank\Webpay\Configuration;
function createSoapTransaction($buyOrder, $amount) {
$config = Configuration::forTestingWebpayPlusNormal();
$transaction = (new Webpay($config))->getNormalTransaction();
return $transaction->initTransaction(
$amount,
$buyOrder,
session_id(),
TU_URL_DE_TRANSICION, // esto nunca supe para qué
TU_URL_FINAL
);
}Note that in both APIs the relevant are $buyOrderand $amount. The rest is removed from constants (your url) or are ephemeral values (the session)You want to make an order board that has those fieldsI want to store the Orders of Purchase ..., with the fields •order •cantity •price •totalGreat, as long as I order to buy it is unique and I wish it to the primary key. In that case, a very rough example using PDO and without attempting to capture exceptions (I was fortunately given the job of using prepared sentences!) would be $ordencompra=time(); // esto no lo uses en producción!
$precio = 100; //tú sabrás cuanto vale
$cantidad = 2; // y cuantas unidades son
$total = $precio * $cantidad;
With that data you could both generate a transaction and save on your table.But that board wouldn't be complete without the Token field.When you create that transaction it will give you a token:// en rest
$token = createRestTransaction($ordencompra,$total)->getToken();
// o en SOAP
$token = createSoapTransaction($ordencompra,$total)->token;
And you as you should know with that token do a form where the client ends on the paybridge.Just before that you create the register because, understandably, you want to store even the cases where the process is not completed.(again, I will encapsulate you in a function). The $conn parameter is the connection to your BBDD in this mock example function storeOrdenCompra($conn, $ordencompra, $cantidad, $precio, $token)
$stmt = $conn->prepare(
"INSERT INTO ordenes (ordencompra,cantidad,precio,total,token)
values (:buyOrder, :cantidad, :precio,:amount, :token)"
);
$stmt->execute([
':ordencompra'=>$ordencompra,
':cantidad'=>$cantidad,
':precio'=>$precio,
':amount'=>$cantidad*$precio,
':token'=>$token
]);
}Besides, you should put a field on your board. status and time marks created_at and updated_at.Step 2: Verify the TransactionOn the happy way the client completes the procedure and Transbank returns it to the URL where you expect that answer. That request that transbank makes to your site contains a parameter token_ws.In the SOAP version it was in the body of a POST request. In the REST version they changed it or will change it to go in the query string of a GET request.Whatever, If you have the $token you can execute the last step that is to acknowledge receipt of the operation. If you don't do it in 30 seconds, eventually Transbank will reverse. Step 2 needs only the token, and cannot be done without the token.If you use the Rest APIIn Rest you can use One time method commit // Rest
use Transbank\Webpay\WebpayPlus\Transaction;
$response = Transaction::commit($token);
That response has the form of an instance ( https://github.com/TransbankDevelopers/transbank-sdk-php/blob/master/src/Webpay/WebpayPlus/TransactionCommitResponse.php ) from which you can get many data (some already have them, others are irrelevant, others do matter a lot), from which the status tells you whether or not the transaction happened. https://www.transbankdevelopers.cl/referencia/webpay#confirmar-una-transaccion-webpay-plus :fieldmeaningstatusTransaction status (INITIALIZED, AUTHORIZED, REVERSED, FAILED, NULLIFIED, PARTIALLY_NULLIFIED, CAPTURED). Maximum length: 64----The commit method executes a PUT request to Transbank, but if you already executed it or want to know the status before commit, you can use $response = Transaction::getStatus($token);
Executing an GET type request and returning an instance of https://github.com/TransbankDevelopers/transbank-sdk-php/blob/master/src/Webpay/WebpayPlus/TransactionStatusResponse.php which is almost 100% identical to the commit response except because the last 4 digits of the card are obtained with the method getCardNumber in one and with getCardDetails in the other.I leave the example of Transbank where you can see the methods $response$response->getVci();
$response->getAmount();
$response->getStatus();
$response->getBuyOrder();
$response->getSessionId();
$response->getCardDetail();
$response->getAccountingDate();
$response->getTransactionDate();
$response->getAuthorizationCode();
$response->getPaymentTypeCode();
$response->getResponseCode();
$response->getInstallmentsAmount();
$response->getInstallmentsNumber();
$response->getBalance();If you use the SOAP APIIn soap the method is used https://www.transbankdevelopers.cl/referencia/webpay-soap?l=php#confirmar-una-transaccion-webpay-plus-normal // Soap
use Transbank\Webpay\Webpay;
use Transbank\Webpay\Configuration;
$config = Configuration::forTestingWebpayPlusNormal();
$transaction = (new Webpay($config))->getNormalTransaction();
$result = $transaction->getTransactionResult($token)
This method obtains the status of authorization of the transaction (equivalent to the getStatus) but below launches a second request where you call the method acknowledgeTransaction I might throw a mistake if you've checked it before.The important thing is status So let's not get distracted. The $result He has a certain output and this in turn a certain responseCode.$output = $result->detailOutput;
$status = $output->responseCode;
The status=0 indicates success. All others are mistakes with varying degrees of mysteryCodeMeaning0Approved transaction-1Transaction Rejection - Reintente (Possible error in transaction data entry)-2Transaction Rejection (It occurred fault when processing the transaction. This rejection message is related to card parameters and/or your associated account)-3Transaction Error (Interno Transbank)-4Broadcast (Rejected by the issuer)-5Reject - Possible Fraud (Transaction with risk of fraud)I leave the example that Transbank puts because the properties of $result and $output come outif ($output->responseCode == 0) {
// Transaccion exitosa, puedes procesar el resultado
// con el contenido de las variables $result y $output.
$result->buyOrder;
$result->sessionId;
$result->cardDetail->cardNumber;
$result->cardDetail->cardExpirationDate;
$result->accountingDate;
$result->transactionDate;
$result->vci;
$result->urlRedirection;
$output->authorizationCode;
$output->paymentType;
$output->amount;
$output->sharesNumber;
$output->commerceCode;
$output->buyOrder;
}Update the registration in BBDDWith that status you can update the record, you've identified using the token. In practice, with just knowing a token you can already enter the entire flow "Step 2"Step 3 Managing interrupted transactionsIn REST the flow, if not completed https://www.transbankdevelopers.cl/documentacion/webpay-plus#crear-una-transaccion , it is considered failed. The SOAP API documentation https://www.transbankdevelopers.cl/referencia/webpay-soap#otros-flujos but I believe the REST documentation more updatedThat means:For the customer: that if you started a payment on your site, and when you arrived at the Webpay walkway you went to make a sandwich, if you delay more than that time you will have to start again. It is redirected to your final URL where your backend will see that the transaction failed.For the store: that if a payment process appears as pending in Transbank and passed that period, you do not need to keep updating it over and over. You can already give it the wrong way.Knowing that you can schedule a cron that every 5 minutes will bring all the records that were created more than 5 minutes ago and remain without status.I leave a possible structure for the table, where the primary key is the purchase order. It's time to define it as an auto-incremental I'm using:create table ordenes
(ordencompra bigint(21) unsigned
default uuid_short()
not null
primary key
-- etc etc
)
and later I will explain why the extra effort would be worthcreate table ordenes
(
ordencompra bigint(21) unsigned
default uuid_short()
not null
primary key,
precio float null,
cantidad int null,
total int null,
token varchar(64) null,
status int null,
created_at datetime default current_timestamp()
not null,
updated_at datetime default current_timestamp()
not null on update current_timestamp(),
constraint token unique (token)
);So the cases that were left "abandoned" are those whose created_at have more than 5 minutes and whose status be null. SELECT token from ordenes
WHERE created_at < date_sub( current_timestamp, interval 300 second )
AND status is null
For each of them you can call commit or getTransactionResult and update them, probably as failed, because as I said above, with the token you know everything4. Optionally, generate unique buyOrdersIt is good practice to delegate the generation of unique IDs instead of arming them oneself. I suggest this considering that if you use the timestamp or a self-incremental, in the development environment where we all have the same commerceCodesooner or later there will be collisions with the buyOrders of other developers. In production, where you have a unique commerceCode, you might be making it easier for a malicious user to list your transactions.In updating the elegant way to generate a truly unique identifier is to use UUIDs. The bad thing is that Transbank only accepts 26 characters at the buyOrder (and uuids need 36). As a substitute you could use for example https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_uuid-short . // en vez de $ordencompra = time();
$ordencompra=$conn->query('SELECT uuid_short()')->fetch(\PDO::FETCH_COLUMN);
Without recommending the UUID_SHORT over another approach, I limit myself to contextualizing that these are a simpler relative of the true UUIDs. Combining a server id, plus its start timestamp, plus a global auto-incremental, you get a number of 17 figures that is unique in all databases of all the servers of the same cluster.In theory you can repeat a UUID_SHORT https://stackoverflow.com/questions/12818671/is-mysql-uuid-short-comparable-to-uuid# :you have more than 255 servers (the server id 256 would be 1)servers sharing ID start with less than 1 second differencegenerates more than 16M of UUID_SHORT per secondWe assume that all the developers we use Transbank implement this approach, and we all have a single server with id=1, we cross our fingers so that we haven't started mysql at the same time or distribute the 16 million.EDIT: Update transaction dateSurely there's a lot of transaction data that you want to persist, and it doesn't come to the case to stop me each. (authorizationCode It is very important to keep it if later you want to completely or partially cancel the transaction, but it is possible that more than someone will cause confusion the transaction date that returns these methods since the format requires some transformation to put it into mysql.First, if you add that date with:alter table ordenes
add transaction_date datetime default null null after token;
If you use RESTIn sub you get the transaction date via $txDate = $result->getTransactionDate();
(and it's the same if that $result comes from the method commit or getStatusThese two classes are almost identical. You told me that the date obtained by this method was something like: 2021-01-11T02:47:27.237-03:00
If you use SOAPIn the Soap API the output getTransactionResult has an equivalent property: $txDate = $result->transactionDate;
According to SOAP documentation the date comes in an exotic medium formatfielddescriptionaccountingDateDate of authorization. Length: 4, MMDD formattransactionDateDate and time of authorization. Length: 6, format: MMDDHHmmI guess they changed opion, because today they respond in format https://en.wikipedia.org/wiki/ISO_8601 2021-01-31T07:44:39.579Z,
The ISO date always refers to UTC. That Z is taken as if I said 2021-01-31T07:44:39.579+00:00FinallyWhether it comes with the explicit offset time or is inferred from the ISO format, they are parseable strings for PHP.$dateTimeInstance = \DateTime::createFromFormat(
'Y-m-d\TH:i:s.uT',
'2021-01-11T02:47:27.237-03:00'
);
or$dateTimeInstance = \DateTime::createFromFormat(
'Y-m-d\TH:i:s.u\Z',
'2021-01-31T07:44:39.579Z'
);
With that I have a DateTime object that I can manipulate, first adjusting it to the UTC time zone (unless you really want to use local date) $dateTimeInstance
->setTimezone(new DateTimeZone('UTC'));
And then return it to string with the format that MySQL likes $created_at_nice = $dateTimeInstance
->format('Y-m-d H:i:s');
That vegetates the formatted date for mysql. echo \DateTime::createFromFormat(
'Y-m-d\TH:i:s.uT',
'2021-01-11T02:47:27.237-03:00'
)->setTimezone(
new DateTimeZone('UTC')
)->format('Y-m-d H:i:s');
// imprime: 2021-01-11 05:47:27
And what is Transbank?For readers of countries where it does not operate https://es.wikipedia.org/wiki/Transbank :Transbank is the only company in Chile authorized to process credit cards. This since 1989 when society purchased the representation of VISA and Mastercard https://es.wikipedia.org/wiki/Bancard . Its exclusivity and property structure almost 100% bank https://es.wikipedia.org/wiki/Transbank#Controversias The API, which stagnated for a few years, has been modernizing vertiginosmente since 2019, going from SOAP to REST, adding new payment modalities and publishing SDKs for more and more languages, amen of plugins for WooCommerce, Magento and almost all the SaaS ecommerce. All that is more or less well documented in https://www.transbankdevelopers.cl/#