-
-
Save rizdaprasetya/9d16893578d600a03075939ef74c5c1f to your computer and use it in GitHub Desktop.
Midtrans 3ds new flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
$client_key = 'SB-Mid-client-61XuGAwQ8Bj8LxSS'; | |
$server_key = 'SB-Mid-server-GwUP_WGbJPXsDzsNEBRs8IYA'; | |
// This part is handling API `/charge` process triggered from AJAX call by frontend (POST request), return RAW API response | |
if ($_SERVER['REQUEST_METHOD'] === 'POST') { | |
$json_str = file_get_contents('php://input'); | |
$json_obj = json_decode($json_str); | |
// Generated by curl-to-PHP: http://incarnate.github.io/curl-to-php/ | |
$ch = curl_init(); | |
$req_body = "{ \"payment_type\": \"credit_card\", \"transaction_details\": { \"order_id\": \"php-test-".time()."\", \"gross_amount\": 100000 }, \"credit_card\": { \"token_id\": \"".$json_obj->token_id."\", \"authentication\": ".($json_obj->authenticate_3ds?"\"true\"":"\"false\"")." }}"; | |
curl_setopt($ch, CURLOPT_URL, 'https://api.sandbox.midtrans.com/v2/charge'); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_POSTFIELDS, $req_body); | |
curl_setopt($ch, CURLOPT_POST, 1); | |
$headers = array(); | |
$headers[] = 'Accept: application/json'; | |
$headers[] = 'Authorization: Basic '.base64_encode($server_key.':'); | |
$headers[] = 'Content-Type: application/json'; | |
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | |
$result = curl_exec($ch); | |
if (curl_errno($ch)) { | |
echo 'Error:' . curl_error($ch); | |
} | |
curl_close ($ch); | |
echo $result; | |
exit(); | |
} | |
// This part is the frontend, showing HTML+JS page (on GET request) | |
?> | |
<html> | |
<head> | |
<title>Core API Credit Card Frontend Sample</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/themes/prism.css" /> | |
<style type="text/css"> | |
pre{ | |
font-size:75%; | |
} | |
/* Fix for fieldset width overflowing parent width */ | |
fieldset { | |
min-width: 0; | |
max-width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- TODO change data-environment to `production` for Production mode --> | |
<!-- TODO change data-client-key to your production client key for Production mode --> | |
<script id= "midtrans-script" src="https://api.midtrans.com/v2/assets/js/midtrans-new-3ds.min.js" data-environment="sandbox" data-client-key="<?php echo $client_key ?>" type="text/javascript"></script> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/picomodal/3.0.0/picoModal.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.15.0/prism.js"></script> | |
<p>This page is to further demonstrate how to implement frontend processes for Credit Card transactions</p> | |
<form id="token-form"> | |
<fieldset> | |
<legend>1. Get Card Token Step</legend> | |
<p> | |
<label>Card Number</label> | |
<input id="card-number" value="4811 1111 1111 1114" size="23" type="text" autocomplete="off" /> | |
</p> | |
<p> | |
<label>Expiration (MM/YYYY)</label> | |
<input id="card-expiry-month" value="12" placeholder="MM" size="2" type="text" /> | |
<span> / </span> | |
<input id="card-expiry-year" value="2020" placeholder="YYYY" size="4" type="text" /> | |
</p> | |
<p> | |
<label>CVV</label> | |
<input id="card-cvv" value="123" size="4" type="password" autocomplete="off" /> | |
</p> | |
<button class="submit-button" type="submit">Process</button> | |
<p> | |
<label>Result card token_id</label> | |
<input type="text" id="token_id_result"> | |
</p> | |
<p> | |
<label>JS implementation:</label> | |
<pre><code class="language-html" id="token-implementation-lib"></code></pre> | |
<pre><code class="language-javascript" id="token-implementation"></code></pre> | |
</p> | |
</fieldset> | |
</form> | |
<br> | |
<form id="charge-form"> | |
<fieldset> | |
<legend>2. Charge process is done via Backend</legend> | |
<p> | |
<label>Card Token ID</label> | |
<input type="text" id="token_id" placeholder="481111-1114-0b9ddf4d-eb29-4d29-8750-15555531d2e7" required> | |
</p> | |
<p> | |
<label>3DS Authentication enabled</label> | |
<input type="checkbox" id="authenticate_3ds" checked> | |
</p> | |
<button class="submit-button" type="submit">Process</button> | |
<p> | |
<label>Result redirect url (if 3DS)</label> | |
<input type="text" id="redirect_url_result"> | |
</p> | |
<small>Check <a href="https://github.com/Midtrans/midtrans-nodejs-client/blob/fc7483503452c41aad8a69df28ebe57aef498505/examples/expressApp/app.js#L67-L77">this code</a>, section `Using Core API - Credit Card` for the backend implementation</small> | |
<pre><code class="language-javascript">// sample charge in CURL | |
curl -X POST \ | |
https://api.sandbox.midtrans.com/v2/charge \ | |
-H 'Accept: application/json' \ | |
-H 'Authorization: Basic < From your server_key >' \ | |
-H 'Content-Type: application/json' \ | |
-d '{ | |
"payment_type": "credit_card", | |
"transaction_details": { | |
"order_id": "order102-2-1562650595", | |
"gross_amount": 100000 | |
}, | |
"credit_card": { | |
"token_id": "< Token ID from Get Token Step >", | |
"authentication": true, | |
"installment_term": 12, // optional for installment | |
"bank": "bni" // optional for installment | |
} | |
}' | |
</code></pre> | |
</fieldset> | |
</form> | |
<br> | |
<form id="authentication-form"> | |
<fieldset> | |
<legend>3. 3DS Authentication Step</legend> | |
<p> | |
<label>Redirect URL</label> | |
<input id="redirect_url" placeholder="https://api.sandbox.veritrans.co.id/v2/token/rba/redirect/481111-1114-004cebd3-8fb0-48f7-bf49-eb4e5df54602" size="35" type="text" required/> | |
</p> | |
<button class="submit-button" type="submit">Process</button> | |
<p> | |
<label>Result:</label> | |
<pre><code id="result" class="language-javascript">...</code></pre> | |
</p> | |
<p> | |
<label>JS implementation:</label> | |
<pre><code class="language-javascript" id="authentication-implementation"></code></pre> | |
</p> | |
</fieldset> | |
</form> | |
<!-- JS implementation to get card token --> | |
<script type="text/javascript" id="js-card-token"> | |
document.querySelector("#token-form").onsubmit = function(e){ | |
var card = { | |
"card_number": document.querySelector("#card-number").value, | |
"card_exp_month": document.querySelector("#card-expiry-month").value, | |
"card_exp_year": document.querySelector("#card-expiry-year").value, | |
"card_cvv": document.querySelector("#card-cvv").value, | |
}; | |
MidtransNew3ds.getCardToken( | |
card, | |
{ | |
onSuccess: function(response){ | |
// Success to get card token_id, implement as you wish here: | |
document.querySelector("#token_id_result").value = response.token_id; | |
document.querySelector("#token_id").value = response.token_id; | |
}, | |
onFailure: function(response){ | |
// Fail to get card token_id | |
alert(`Fail to get card token_id: ${JSON.stringify(response)}`); | |
console.log(`Fail to get card token_id: ${JSON.stringify(response)}`); | |
} | |
} | |
); | |
return e.preventDefault() && 0; // prevent form submit | |
} | |
</script> | |
<!-- JS implementation to handle 3DS authentication popup --> | |
<script type="text/javascript" id="js-authentication"> | |
document.querySelector("#authentication-form").onsubmit = function(e){ | |
var redirect_url = document.querySelector("#redirect_url").value; | |
// Utilize MidtransNew3Ds.authenticate function to perform 3DS authentication, | |
// and handle the success/failure result | |
MidtransNew3ds.authenticate( | |
redirect_url, | |
{ | |
performAuthentication: function(redirect_url){ | |
// open iframe to display 3ds authentication redirect_url to customer | |
popupModal.openPopup(redirect_url); | |
}, | |
onSuccess: function(response){ | |
// 3ds authentication success, implement payment success scenario | |
popupModal.closePopup(); | |
document.querySelector("#result").innerText = JSON.stringify(response,null,2); | |
}, | |
onFailure: function(response){ | |
// 3ds authentication failure, implement payment failure scenario | |
popupModal.closePopup(); | |
document.querySelector("#result").innerText = JSON.stringify(response,null,2); | |
}, | |
onPending: function(response){ | |
// transaction is pending, transaction result will be notified later via POST notification | |
popupModal.closePopup(); | |
document.querySelector("#result").innerText = JSON.stringify(response,null,2) | |
} | |
} | |
); | |
return e.preventDefault() && 0; // prevent form submit | |
} | |
/** | |
* Alternatively instead of opening 3ds authentication redirect_url using iframe, | |
* you can also redirect customer using: | |
``` | |
MidtransNew3ds.redirect(redirect_url, { | |
callbackUrl : 'https://mywebsite.com/finish_3ds' | |
}); | |
``` | |
* | |
*/ | |
/** | |
* helper functions to open Iframe popup, you may replace this with your own method to open iframe | |
* PicoModal library is used: | |
* <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/picomodal/3.0.0/picoModal.js"><\/script> | |
*/ | |
var popupModal = (function(){ | |
var modal = null; | |
return { | |
openPopup(url){ | |
modal = picoModal({ | |
content:'<iframe frameborder="0" style="height:90vh; width:100%;" src="'+url+'"></iframe>', | |
width: "75%", | |
closeButton: false, | |
overlayClose: false, | |
escCloses: false | |
}).show(); | |
}, | |
closePopup(){ | |
try{ | |
modal.close(); | |
} catch(e) {} | |
} | |
} | |
}()); | |
</script> | |
<!-- Unrelated script, for demo purpose --> | |
<script type="text/javascript"> | |
document.querySelector("#token-implementation-lib").innerHTML = '<!-- Include Midtrans JS library --> \n'+ document.querySelector("#midtrans-script") | |
.outerHTML | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/\\/g, ""); | |
document.querySelector("#token-implementation").innerHTML = document.querySelector("#js-card-token") | |
.innerHTML | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/\\/g, ""); | |
document.querySelector("#authentication-implementation").innerHTML = document.querySelector("#js-authentication") | |
.innerHTML | |
.replace(/</g, "<") | |
.replace(/>/g, ">") | |
.replace(/\\/g, ""); | |
document.querySelector("#charge-form").onsubmit = function(e){ | |
popupModal.openPopup('data:text/html,requesting...'); | |
fetch("index.php", { | |
method : "POST", | |
body: JSON.stringify({ | |
"token_id" : document.querySelector("#token_id").value, | |
"authenticate_3ds" : document.querySelector('#authenticate_3ds').checked | |
}), | |
headers: {'Content-Type': 'application/json'}, | |
}) | |
.then(function(response) { return response.json(); }) | |
.then(function(responseObj){ | |
if(responseObj.redirect_url){ | |
document.querySelector("#redirect_url").value = responseObj.redirect_url; | |
document.querySelector("#redirect_url_result").value = responseObj.redirect_url; | |
} else if (responseObj.status_code){ | |
document.querySelector("#result").innerText = JSON.stringify(responseObj,null,2); | |
popupModal.closePopup(); | |
document.querySelector("#result").scrollIntoView(); | |
} | |
}) | |
.catch(function(err){ | |
alert(`Fail to get charge response: ${JSON.stringify(err)}`); | |
console.log(`Fail to get charge response: ${JSON.stringify(err)}`); | |
}) | |
.finally(function(){ | |
popupModal.closePopup(); | |
}) | |
return e.preventDefault() && 0; // prevent form submit | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment