Browse Source

braintree methods

master
frank 2 years ago
parent
commit
6c6ff885d8
  1. 2
      braintree.md
  2. 710
      static/braintree-sample.html

2
braintree.md

@ -1,4 +1,3 @@
braintree.md
# 获取authorization
@ -29,6 +28,7 @@ braintree.md
### Response
* authorization是用来在付款页面调用braintree js sdk,用户填信用卡信息
* [HTML付款页面示例](./static/braintree-sample.html)
```json
{

710
static/braintree-sample.html

@ -0,0 +1,710 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!--jquery为了调用服务端获取token-->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.71.0/js/client.js"></script>
<script src="https://js.braintreegateway.com/web/3.71.0/js/hosted-fields.js"></script>
<script src="https://js.braintreegateway.com/web/3.71.0/js/data-collector.min.js"></script>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css"
integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<title>Payment</title>
<style>
body {
background-color: #efecec;
}
button {
width: 97px;
height: 40px;
padding: 10px;
font-size: 16px;
color: #fff;
background-color: #47A2FF;
border: none;
border-radius: 5px;
}
.loading {
margin: 20% auto;
text-align: center;
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
-webkit-animation: spin 2s linear infinite;
animation: spin 2s linear infinite;
z-index: 1000;
display: none;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.main {
margin: 50px auto;
width: 90%;
max-width: 800px;
background-color: #fff;
padding: 10px 20px;
min-height: 500px;
border-radius: 10px;
}
/*.btn{width:100%;text-align: center;display: none;}*/
/*hosted field*/
body {
background-color: #fff;
padding: 15px;
}
.toast {
position: fixed;
top: 15px;
right: 15px;
z-index: 9999;
}
.bootstrap-basic {
background: white;
}
/* Braintree Hosted Fields styling classes*/
.braintree-hosted-fields-focused {
color: #495057;
background-color: #fff;
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.braintree-hosted-fields-focused.is-invalid {
border-color: #dc3545;
box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
}
.row {
margin-bottom: 40px;
}
.alert-fixed {
position: fixed;
top: 10px;
left: 50%;
width: 10%;
z-index: 9999;
border-radius: 0px;
padding: 5px;
}
</style>
</head>
<body>
<input type="hidden" name="_token" id="_token" value="{{.Authorization}}" />
<input type="hidden" name="_submitUrl" id="_submitUrl" value="http://localhost:17764/api/homeapi/process" />
<input type="hidden" name="_amt" id="_amt" value="{{.Amt}}" />
<input type="hidden" name="_cur" id="_cur" value="{{.Currency}}" />
<input type="hidden" name="_intent" id="_intent" value="{{.Intent}}" />
<input type="hidden" name="_flow" id="_flow" value="{{.Flow}}" />
<input type="hidden" name="_transactionNo" id="_transactionNo" value="{{.TransactionNo}}" />
<input type="hidden" name="_vendor" id="_vendor" value="{{.Vendor}}" />
<div class="alert-primary alert-fixed" style="text-align: center; display: none;" role="alert">
<i class="fa fa-check-circle-o" aria-hidden="true" style="padding: 0 5px;"></i>Apply successfully!
</div>
<div class="loading" id="loading"></div>
<div class="main" id="main">
<div class="info">
<p>Transaction No: {{.TransactionNo}}</p>
<p>Amount: {{.Amt}}</p>
<p>Currency: {{.Currency}}</p>
</div>
<div class="bootstrap-basic">
<form class="needs-validation" novalidate="">
<div class="row">
<div class="form-item col-sm-12 mb-3 is-required">
<label for="first-name">First Name</label>
<input class="form-control" id="first-name">
<!-- <small class="text-muted">First name as displayed on card</small>-->
<div class="invalid-feedback">
First Name is required
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="last-name">Last Name</label>
<input class="form-control" id="last-name">
<!-- <small class="text-muted">Last name as displayed on card</small>-->
<div class="invalid-feedback">
Last Name is required
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="email">Email</label>
<input type="email" class="form-control" id="email" placeholder="[email protected]">
<div class="invalid-feedback">
Please enter a valid email
</div>
</div>
<div class="form-item col-sm-12 mb-3 ">
<label for="countryId">Country</label>
<select class="countries form-control" id="countryId">
<option value="">Select Country</option>
</select>
<div class="invalid-feedback">
Please select country.
</div>
</div>
<!--<div class="form-item col-sm-12 mb-3 is-required">-->
<!--<label for="phone">Phone</label>-->
<!--<input class="form-control" id="phone">-->
<!--<div class="invalid-feedback">-->
<!--Please enter phone.-->
<!--</div>-->
<!--</div>-->
<div class="form-item col-sm-12 mb-3 ">
<label for="stateId">State</label>
<select class="states form-control" id="stateId">
<option value="">Select State</option>
</select>
<div class="invalid-feedback">
Please select state.
</div>
</div>
<div class="form-item col-sm-12 mb-3 ">
<label for="cityId">City</label>
<select class="cities form-control" id="cityId">
<option value="">Select City</option>
</select>
<div class="invalid-feedback">
Please select city.
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="streetAddress">StreetAddress</label>
<input class="form-control" id="streetAddress">
<div class="invalid-feedback">
Please enter streetAddress.
</div>
</div>
<div class="form-item col-sm-12 mb-3">
<label for="streetAddress2">StreetAddress2</label>
<input class="form-control" id="streetAddress2">
<div class="invalid-feedback">
Please enter streetAddress.
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="postal-code">Post Code</label>
<input class="form-control" id="postal-code">
<div class="invalid-feedback">
Please enter postal code.
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="cc-number">Credit card number</label>
<div class="form-control" id="cc-number"></div>
<div class="invalid-feedback">
Credit card number is required
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="cc-expiration">Expiration</label>
<div class="form-control" id="cc-expiration"></div>
<div class="invalid-feedback">
Expiration date required
</div>
</div>
<div class="form-item col-sm-12 mb-3 is-required">
<label for="cc-expiration">CVV</label>
<div class="form-control" id="cc-cvv"></div>
<div class="invalid-feedback">
Security code required
</div>
</div>
</div>
<hr class="mb-4">
<div class="text-center">
<!-- <button class="btn btn-primary btn-lg" type="submit">Pay with <span id="card-brand">Card</span></button>-->
<button class="btn btn-primary btn-lg" type="submit">Pay</button>
</div>
</form>
</div>
</div>
<script>
// 数据
let curPaymentMethod = '';
const paymentMethods = {
'CreditCard': 'credit_card',
'PayPalAccount': 'paypal_account',
'VenmoAccount': 'venmo_account',
'ApplePayCard': 'apple_pay_card',
'AndroidPayCard': 'android_pay_card', //google pay
}
const token = document.getElementById('_token').value;
const amount = document.getElementById('_amt').value;
const intent = document.getElementById('_intent').value;
const currency = document.getElementById('_cur').value;
const transactionNo = document.getElementById('_transactionNo').value;
// const url = document.getElementById("_submitUrl").value;
const vendor = document.getElementById("_vendor").value;
const url = '/bt/process';
// const vendor = 'creditcard';
//方法
function ajaxObject() {
let xmlHttp;
try {
// Firefox, Opera 8.0+, Safari
xmlHttp = new XMLHttpRequest();
}
catch (e) {
// Internet Explorer
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("您的浏览器不支持AJAX!");
return false;
}
}
}
return xmlHttp;
}
function ajaxPost(url, data, fnSucceed) {
$.ajax({
type: "POST",
url: url,
data: data,
contentType: "application/x-www-form-urlencoded",
dataType: "json",
success: function (data) {
console.log("success");
fnSucceed(data);
},
error: function (data) {
console.log("error");
},
})
}
function getVendor() {
// 根据vendor 配置braintree 的支付渠道
let curVendor = null;
console.log('vendor is: ', vendor);
switch (vendor) {
case 'paypal':
curVendor = {
paypal: {
flow: 'checkout',
amount: amount,
currency: currency
},
// paypalCredit: {
// flow: 'checkout',
// amount: amount,
// currency: currency
// },
};
break
case 'venmo':
curVendor = {
venmo: { // 只会在移动端出现
allowNewBrowserTab: false
}
};
break
case 'googlepay':
curVendor = {
card: false, // 不显示信用卡
googlePay: {
// googlePayVersion: 2,
// merchantId: 'merchant-id-from-google',
transactionInfo: {
totalPriceStatus: 'FINAL',
totalPrice: amount,
currencyCode: currency
},
allowedPaymentMethods: [{
type: 'CARD',
parameters: {
// We recommend collecting and passing billing address information with all Google Pay
// transactions as a best practice.
billingAddressRequired: true,
billingAddressParameters: {
format: 'FULL'
}
}
}]
},
};
break
case 'applepay':
curVendor = {
applePay: {
displayName: 'Merchant Name',
paymentRequest: {
label: 'Localized Name',
currencyCode: '',
merchantCapabilities: '',
total: amount
}
},
};
break
case 'creditcard':
break
default:
console.log('vendor is: ', vendor);
}
console.log('curVendor info: ', curVendor);
return curVendor
}
function handleIdElementStyle(id, attr, val) {
const el = document.getElementById(id);
if (el) {
el.style[attr] = val;
}
}
function initClient() {
const curVendor = getVendor();
const options = {
authorization: token,
container: '#dropin-container',
...curVendor
};
console.log('options', options);
var form = $('form');
braintree.client.create({
authorization: token,
}, function (err, clientInstance) {
if (err) {
console.error(err);
alert('clientInstance: ' + JSON.stringify(err));
return;
}
braintree.dataCollector.create({
client: clientInstance,
paypal: true
}, function (err, dataCollectorInstance) {
if (err) {
// Handle error in creation of data collector
console.error(err);
alert('clientInstance: ' + JSON.stringify(err));
return;
}
// At this point, you should access the dataCollectorInstance.deviceData value and provide it
// to your server, e.g. by injecting it into your form as a hidden input.
var deviceData = dataCollectorInstance.deviceData;
braintree.hostedFields.create({
client: clientInstance,
styles: {
input: {
// change input styles to match
// bootstrap styles
'font-size': '1rem',
color: '#495057'
}
},
fields: {
// cardholderName: {
// selector: '#cc-name',
// placeholder: 'Name as it appears on your card'
// },
number: {
selector: '#cc-number',
placeholder: '4111 1111 1111 1111'
},
cvv: {
selector: '#cc-cvv',
placeholder: '123'
},
expirationDate: {
selector: '#cc-expiration',
placeholder: 'MM / YYYY'
}
}
}, function (err, hostedFieldsInstance) {
if (err) {
console.error('hostedFieldsInstance: ' + err);
return;
}
function createInputChangeEventListener(element) {
return function () {
validateInput(element);
}
}
function setValidityClasses(element, validity) {
if (validity) {
element.removeClass('is-invalid');
element.addClass('is-valid');
} else {
element.addClass('is-invalid');
element.removeClass('is-valid');
}
}
function validateInput(element) {
// very basic validation, if the
// fields are empty, mark them
// as invalid, if not, mark them
// as valid
if (!element.val().trim()) {
setValidityClasses(element, false);
return false;
}
setValidityClasses(element, true);
return true;
}
var email = $('#email');
function validateEmail() {
var baseValidity = validateInput(email);
if (!baseValidity) {
return false;
}
if (email.val().indexOf('@') === -1) {
setValidityClasses(email, false);
return false;
}
setValidityClasses(email, true);
return true;
}
$('.form-item.is-required input').on('blur change', function () {
var $this = $(this);
if ($this.type == 'email') {
validateEmail($this);
}
validateInput($this);
})
$('.form-item.is-required select').on('blur change', function () {
var $this = $(this);
validateInput($this);
})
hostedFieldsInstance.on('validityChange', function (event) {
var field = event.fields[event.emittedBy];
// Remove any previously applied error or warning classes
$(field.container).removeClass('is-valid');
$(field.container).removeClass('is-invalid');
if (field.isValid) {
$(field.container).addClass('is-valid');
} else if (field.isPotentiallyValid) {
// skip adding classes if the field is
// not valid, but is potentially valid
} else {
$(field.container).addClass('is-invalid');
}
});
hostedFieldsInstance.on('cardTypeChange', function (event) {
var cardBrand = $('#card-brand');
var cvvLabel = $('[for="cc-cvv"]');
if (event.cards.length === 1) {
var card = event.cards[0];
// change pay button to specify the type of card
// being used
cardBrand.text(card.niceType);
// update the security code label
cvvLabel.text(card.code.name);
} else {
// reset to defaults
cardBrand.text('Card');
cvvLabel.text('CVV');
}
});
form.submit(function (event) {
event.preventDefault();
//显示loading
handleIdElementStyle('loading', 'display', 'block');
var formIsInvalid = false;
// perform validations on the non-Hosted Fields
// inputs
if (!validateEmail()) {
formIsInvalid = true;
}
var $inputs = $('.form-item.is-required input')
for (var i = 0; i < $inputs.length; i++) {
if (!validateInput($inputs.eq(i))) {
formIsInvalid = true
}
}
var $selects = $('.form-item.is-required select')
for (var i = 0; i < $selects.length; i++) {
if (!validateInput($selects.eq(i))) {
formIsInvalid = true
}
}
var state = hostedFieldsInstance.getState();
// Loop through the Hosted Fields and check
// for validity, apply the is-invalid class
// to the field container if invalid
Object.keys(state.fields).forEach(function (field) {
if (!state.fields[field].isValid) {
$(state.fields[field].container).addClass('is-invalid');
formIsInvalid = true;
}
});
if (formIsInvalid) {
// skip tokenization request if any fields are invalid
//隐藏loading
handleIdElementStyle('loading', 'display', 'none');
return;
}
const firstName = document.getElementById('first-name').value;
const lastName = document.getElementById('last-name').value;
const countryName = document.getElementById('countryId').value;
const stateName = document.getElementById('stateId').value;
const city = document.getElementById('cityId').value;
const address = document.getElementById('streetAddress').value;
const address2 = document.getElementById('streetAddress2').value;
let streetAddressArr = []
stateName && streetAddressArr.push(streetAddressArr)
city && streetAddressArr.push(city)
address && streetAddressArr.push(address)
address2 && streetAddressArr.push(address2)
const postalCode = document.getElementById('postal-code').value;
const email = document.getElementById('email').value;
const tokenizeOption = {
cardholderName: firstName + ' ' + lastName,
billingAddress: {
firstName: firstName,
lastName: lastName,
cardholderName: '2333',
// company: 'Company',
streetAddress: streetAddressArr.join(','),
postalCode: postalCode,
// extendedAddress: 'Unit 1',
// passing just one of the country options is sufficient to
// associate the card details with a particular country
// valid country names and codes can be found here:
// https://developers.braintreepayments.com/reference/general/countries/ruby#list-of-countries
countryName: 'United States',
countryCodeAlpha2: 'US',
countryCodeAlpha3: 'USA',
countryCodeNumeric: '840'
}
};
hostedFieldsInstance.tokenize(tokenizeOption, function (err, payload) {
if (err) {
console.error(err);
alert('payload: ' + JSON.stringify(err));
//隐藏loading
handleIdElementStyle('loading', 'display', 'none');
return;
}
// This is where you would submit payload.nonce to your server
// $('.toast').toast('show');
// you can either send the form values with the payment
// method nonce via an ajax request to your server,
// or add the payment method nonce to a hidden inpiut
// on your form and submit the form programatically
// $('#payment-method-nonce').val(payload.nonce);
// form.submit()
// 通过payload来给curPaymentMethod赋值
curPaymentMethod = paymentMethods[payload.type];
// Submit payload.nonce to your server
const temp = payload;
const params = "paymentMethodNonce="+payload.nonce+"&transactionNo="+transactionNo+
"&paymentMethod="+curPaymentMethod+"&firstName="+firstName+"&lastName="+
lastName+"&addressLine1="+streetAddress+"&postalCode="+postalCode+"&email="+email+
"&deviceData=" + deviceData;
ajaxPost(url, params, function (data) {
const result = data;
if ("success" === result.result.status) {
handleIdElementStyle('loading', 'display', 'none');
$('.alert-primary').show(500);
setTimeout(function () {
// handleIdElementStyle('main','display','block');
window.location.href = "";
}, 1500);
} else {
handleIdElementStyle('loading', 'display', 'none');
handleIdElementStyle('main', 'display', 'block');
alert(result.ret_msg)
location.reload();
}
})
});
});
});
});
});
}
//=====================start project=======================
//
initClient()
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
Loading…
Cancel
Save