- Minimum Gereksinimler
- Kurulum
- Ornek 3D Secure Odeme
- Konfigurasyon Yapısı ve Örnekler
- API ve 3D Form verisini degiştirme
- PHP >= 7.4
- mews/pos ^1.3
- Symfony 4|5|6|7
-
$ composer require mews/pos-bundle
config/packages/mews_pos.yaml
dosyası oluşturun- ve içine bu alttaki config örneği ekleyin:
mews_pos: banks: estpos: # herhangi unique bir isim gateway_class: Mews\Pos\Gateways\EstV3Pos lang: !php/const Mews\Pos\PosInterface::LANG_TR # optional test_mode: false #optional, default: false; credentials: payment_model: !php/const Mews\Pos\PosInterface::MODEL_3D_SECURE merchant_id: 700xxxxxx user_name: ISXXXXXX #EstPos: kullanici adi user_password: ISYYYYY #EstPos: kullanici sifresi enc_key: TRPXXXXX gateway_endpoints: # ilgili ortamin (test/prod) URL'leriyle degistiriniz: payment_api: 'https://entegrasyon.asseco-see.com.tr/fim/api' gateway_3d: 'https://entegrasyon.asseco-see.com.tr/fim/est3Dgate' gateway_3d_host: 'https://sanalpos.sanalakpos.com.tr/fim/est3Dgate' # optional, 3D Host ödemeler için zorunlu yapikredi: gateway_class: Mews\Pos\Gateways\PosNet credentials: payment_model: !php/const Mews\Pos\PosInterface::MODEL_3D_SECURE merchant_id: 670XXXXXXX # Üye İşyeri Numarası. terminal_id: 67XXXXXX # Üye İşyeri Terminal Numaras user_name: 27XXX # Üye İşyeri POSNET Numarası enc_key: 10,92,92,02,02,02,02,02,01 # Şifreleme anahtar gateway_endpoints: payment_api: 'https://setmpos.ykb.com/PosnetWebService/XML' gateway_3d: 'https://setmpos.ykb.com/3DSWebService/YKBPaymentService'
<?php
namespace App\Controller;
use Mews\Pos\Entity\Card\CreditCardInterface;
use Mews\Pos\Exceptions\CardTypeNotSupportedException;
use Mews\Pos\Exceptions\CardTypeRequiredException;
use Mews\Pos\Exceptions\HashMismatchException;
use Mews\Pos\Factory\CreditCardFactory;
use Mews\Pos\Gateways\PayFlexV4Pos;
use Mews\Pos\PosInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
#[Route('/payment/3d')]
class SingleBankThreeDSecurePaymentController extends AbstractController
{
private string $paymentModel = PosInterface::MODEL_3D_HOST;
public function __construct(
/**
* mews_pos.yaml'da birden fazla banka configurasyonu varsa, ilki default olarak inject edilecek
*/
private PosInterface $pos,
/**
* spesifik bir bankayi inject etmek istiyorsak:
*/
private PosInterface $yapikrediTest,
private UrlGeneratorInterface $urlGenerator,
/**
* birden fazla banka oldugunda bu sekilde hepsine erisebilirsiniz
* @var PosInterface[]
*/
#[TaggedIterator('mews_pos.gateway')]
private iterable $banks,
)
{
// foreach ($this->banks as $bank) {
// if ('estpos' === $bank->getAccount()->getBank()) {
// // todo
// }
// }
}
/**
* Kullanicidan kredi kart bilgileri alip buraya POST ediyoruz
*/
#[Route('/form', name: 'single_bank_payment_3d_redirect_form', methods: ['POST'])]
public function form(Request $request)
{
$session = $request->getSession();
$transaction = $request->get('tx', PosInterface::TX_TYPE_PAY_AUTH);
$callbackUrl = $this->urlGenerator->generate('single_bank_payment_3d_response', [], UrlGeneratorInterface::ABSOLUTE_URL);
$order = $this->createNewOrder(
$this->paymentModel,
$callbackUrl,
$request->getClientIp(),
$request->get('currency', PosInterface::CURRENCY_TRY),
$request->get('installment'),
$request->get('lang', PosInterface::LANG_TR)
);
$session->set('order', $order);
$card = $this->createCard($this->pos, $request->request->all());
/**
* PayFlex'te provizyonu (odemeyi) tamamlamak icin tekrar kredi kart bilgileri isteniyor,
* bu yuzden kart bilgileri kaydediyoruz
*/
if ($this->pos::class === PayFlexV4Pos::class) {
$session->set('card', $request->request->all());
}
$session->set('tx', $transaction);
try {
$formData = $this->pos->get3DFormData($order, $this->paymentModel, $transaction, $card);
} catch (\Throwable $e) {
dd($e);
}
return $this->render('redirect-form.html.twig', [
'formData' => $formData,
]);
}
/**
* kullanici bankadan geri buraya redirect edilir
*/
#[Route('/response', name: 'single_bank_payment_3d_response')]
public function response(Request $request)
{
$session = $request->getSession();
$transaction = $session->get('tx', PosInterface::TX_TYPE_PAY_AUTH);
// bankadan POST veya GET ile veri gelmesi gerekiyor
if (($request->getMethod() !== 'POST')
// PayFlex-CP GET request ile cevapliyor
&& ($request->getMethod() === 'GET' && ($this->pos::class !== \Mews\Pos\Gateways\PayFlexCPV4Pos::class || [] === $request->query->all()))
) {
return new RedirectResponse($request->getBaseUrl());
}
$card = null;
if ($this->pos::class === \Mews\Pos\Gateways\PayFlexV4Pos::class) {
// bu gateway için ödemeyi tamamlarken tekrar kart bilgisi lazım.
$savedCard = $session->get('card');
$card = $this->createCard($this->pos, $savedCard);
$session->remove('card');
}
$order = $session->get('order');
if (!$order) {
throw new \Exception('Sipariş bulunamadı, session sıfırlanmış olabilir.');
}
try {
$this->pos->payment($this->paymentModel, $order, $transaction, $card);
} catch (HashMismatchException $e) {
dd($e);
} catch (\Exception|\Error $e) {
dd($e);
}
if ($this->pos->isSuccess()) {
echo 'success';
dd($this->pos->getResponse());
} else {
dd($response);
}
}
private function createNewOrder(
string $paymentModel,
string $callbackUrl,
string $ip,
string $currency,
?int $installment = 0,
string $lang = PosInterface::LANG_TR
): array
{
$orderId = date('Ymd').strtoupper(substr(uniqid(sha1(time())), 0, 4));
$order = [
'id' => $orderId,
'amount' => 10.01,
'currency' => $currency,
'installment' => $installment,
'ip' => \filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ? $ip : '127.0.0.1',
];
if (\in_array($paymentModel, [
PosInterface::MODEL_3D_SECURE,
PosInterface::MODEL_3D_PAY,
PosInterface::MODEL_3D_HOST,
PosInterface::MODEL_3D_PAY_HOSTING,
], true)) {
$order['success_url'] = $callbackUrl;
$order['fail_url'] = $callbackUrl;
}
if ($lang) {
//lang degeri verilmezse account (EstPosAccount) dili kullanilacak
$order['lang'] = $lang;
}
return $order;
}
private function createCard(PosInterface $pos, array $card): CreditCardInterface
{
try {
return CreditCardFactory::createForGateway(
$pos,
$card['number'],
$card['year'],
$card['month'],
$card['cvv'],
$card['name'],
$card['type'] ?? null
);
} catch (CardTypeRequiredException|CardTypeNotSupportedException $e) {
dd($e);
} catch (\LogicException $e) {
dd($e);
}
}
}
redirect-form.html.twig
:
<form method="{{ formData.method }}" action="{{ formData.gateway }}" class="redirect-form" role="form">
{% for key, value in formData.inputs %}
<input type="hidden" name="{{ key }}" value="{{ value }}">
{% endfor %}
<div class="text-center">Redirecting...</div>
<hr>
<div class="form-group text-center">
<button type="submit" class="btn btn-lg btn-block btn-success">Submit</button>
</div>
</form>
PHP Sessioni kullanıyorsanız bu ayarları da yapmanız gerekiyor:
# /config/packages/framework.yaml
framework:
session:
cookie_secure: true
cookie_samesite: none
KuveytPos TDV2.0.0 için ekstra veri eklemek zorunludur. Bunun EventListener ile yapabilirsiniz:
<?php
namespace App\EventListener;
use Mews\Pos\Event\RequestDataPreparedEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
/**
* KuveytPos TDV2.0.0 odemenin calismasi icin zorunlu eklenmesi gereken alan var.
*/
#[AsEventListener(event: RequestDataPreparedEvent::class)]
final class KuveytPosV2RequestDataPreparedEventListener
{
public function __invoke(RequestDataPreparedEvent $event): void
{
if ($event->getGatewayClass() !== \Mews\Pos\Gateways\KuveytPos::class) {
return;
}
/**
* ekstra eklenmesi gereken verileri isteseniz $order icine ekleyip sonra o verilere
* $event->getOrder() ile erisebilirsiniz.
*/
$additionalRequestDataForKuveyt = [
'DeviceData' => [
/**
* DeviceChannel : DeviceData alanı içerisinde gönderilmesi beklenen işlemin yapıldığı cihaz bilgisi.
* 2 karakter olmalıdır. 01-Mobil, 02-Web Browser için kullanılmalıdır.
*/
'DeviceChannel' => '02',
],
'CardHolderData' => [
/**
* BillAddrCity: Kullanılan kart ile ilişkili kart hamilinin fatura adres şehri.
* Maksimum 50 karakter uzunluğunda olmalıdır.
*/
'BillAddrCity' => 'İstanbul',
/**
* BillAddrCountry Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki ülke kodu.
* Maksimum 3 karakter uzunluğunda olmalıdır.
* ISO 3166-1 sayısal üç haneli ülke kodu standardı kullanılmalıdır.
*/
'BillAddrCountry' => '792',
/**
* BillAddrLine1: Kullanılan kart ile ilişkili kart hamilinin teslimat adresinde yer alan sokak vb. bilgileri içeren açık adresi.
* Maksimum 150 karakter uzunluğunda olmalıdır.
*/
'BillAddrLine1' => 'XXX Mahallesi XXX Caddesi No 55 Daire 1',
/**
* BillAddrPostCode: Kullanılan kart ile ilişkili kart hamilinin fatura adresindeki posta kodu.
*/
'BillAddrPostCode' => '34000',
/**
* BillAddrState: CardHolderData alanı içerisinde gönderilmesi beklenen ödemede kullanılan kart ile ilişkili kart hamilinin fatura adresindeki il veya eyalet bilgisi kodu.
* ISO 3166-2'de tanımlı olan il/eyalet kodu olmalıdır.
*/
'BillAddrState' => '40',
/**
* Email: Kullanılan kart ile ilişkili kart hamilinin iş yerinde oluşturduğu hesapta kullandığı email adresi.
* Maksimum 254 karakter uzunluğunda olmalıdır.
*/
'Email' => 'xxxxx@gmail.com',
'MobilePhone' => [
/**
* Cc: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait ülke kodu. 1-3 karakter uzunluğunda olmalıdır.
*/
'Cc' => '90',
/**
* Subscriber: Kullanılan kart ile ilişkili kart hamilinin cep telefonuna ait abone numarası.
* Maksimum 15 karakter uzunluğunda olmalıdır.
*/
'Subscriber' => '1234567899',
],
],
];
$requestData = $event->getRequestData();
$requestData = \array_merge_recursive($requestData, $additionalRequestDataForKuveyt);
$event->setRequestData($requestData);
}
}