我用。net Standard 2.0编写了通过Braintree支付的代码。代码使用Braintree 5.2.0 NuGet包。当密码被用于布伦特里的生产账户时,我打算专门进行3D安全支付。我已经编写了一个集成测试,该测试创建了一个客户,为该客户创建了一个支付方法,然后使用生成的Token对该支付方法进行支付。
创建客户的代码是:
public async Task<string> SeedCustomer(IBraintreeConfiguration braintreeConfiguration)
{
BraintreeGateway _braintreeGateway = new BraintreeGateway(
braintreeConfiguration.Environment,
braintreeConfiguration.MerchantId,
braintreeConfiguration.PublicKey,
braintreeConfiguration.PrivateKey
);
CustomerRequest request = new CustomerRequest
{
Email = BraintreeTestConstants.Email,
CustomFields = new Dictionary<string, string>
{
{"account_id", BraintreeTestConstants.AccountId}
}
};
string braintreeCustomerId =
(await _braintreeGateway.Customer.CreateAsync(request)).Target.Id;
return braintreeCustomerId;
}
创建支付方法的代码是:
PaymentMethodRequest paymentMethodRequest = new PaymentMethodRequest
{
PaymentMethodNonce = nonce,
CustomerId = customerId,
BillingAddress = new PaymentMethodAddressRequest
{
FirstName = BraintreeTestConstants.BillingName,
Locality = BraintreeTestConstants.City,
Company = BraintreeTestConstants.CompanyName,
CountryCodeAlpha2 = BraintreeTestConstants.Country,
ExtendedAddress = BraintreeTestConstants.ExtendedAddress,
Region = BraintreeTestConstants.State,
StreetAddress = BraintreeTestConstants.StreetAddress,
PostalCode = BraintreeTestConstants.Zip,
},
Options = new PaymentMethodOptionsRequest
{
VerifyCard = true
}
};
Result<PaymentMethod> result =
await _braintreeGateway.PaymentMethod.CreateAsync(paymentMethodRequest);
return _mapper.Map<AddPaymentMethodResultModel>((CreditCard)result.Target);
信用卡。Token映射到AddPaymentMethodResultModel.CardId.
支付的代码是:
bool useThreeDSecure = true;
TransactionRequest transactionRequest = new TransactionRequest
{
Amount = Amount,
PaymentMethodToken = CardId,
MerchantAccountId = string.IsNullOrWhiteSpace(MerchantAccountId) ? null : MerchantAccountId,
TransactionSource = string.IsNullOrWhiteSpace(TransactionSource) ? null : TransactionSource,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = true,
ThreeDSecure = new TransactionOptionsThreeDSecureRequest
{
Required = useThreeDSecure
}
}
};
if (Address != null)
{
transactionRequest.BillingAddress = new AddressRequest
{
Company = Address.CompanyName,
FirstName = Address.BillingName,
StreetAddress = Address.StreetAddress,
ExtendedAddress = Address.ExtendedAddress,
Locality = Address.City,
Region = Address.State,
PostalCode = Address.Zip,
CountryCodeAlpha2 = Address.Country
};
}
Result<Transaction> result =
await _braintreeGateway.Transaction.SaleAsync(transactionRequest);
当我将TransactionRequest.Options.ThreeDSecure.Required设置为false,在Braintree沙箱环境下执行测试时,支付成功。
当我对Braintree沙箱环境执行测试TransactionRequest.Options.ThreeDSecure.Required设置为true时,支付失败并显示Result。Message ofGateway Rejected: three_d_secure.
我想知道是否有可能通过Braintree沙箱环境与TransactionRequest.Options.ThreeDSecure.Required设置为true成功支付。我尝试使用通过NuGet包创建的付款方式,但没有成功,PaymentMethodNonce为fake-three- secure-visa-full-authentication-nonce。
我想证明3D安全支付可以通过Braintree沙箱环境进行,以确信代码将在Braintree生产环境下工作。
在进行3D安全支付之前,必须实现一个客户端部分来验证拱形卡。
有两种方法可以实现这一点:插入式UI或托管字段。插入式UI提供具有某种样式的表单,而托管字段提供对表单外观的更多控制。我不要求字段是可见的,因为我可以通过JavaScript单独管理数据,所以我使用托管字段。首先,必须生成客户机令牌,以及表示服务器端上的拱顶卡的nonce和bin。实现这一点的代码是:
public async Task<string> GetBraintreeClientToken()
{
string clientToken =
await _braintreeChargeService.GetClientToken();
return clientToken;
}
public async Task<BraintreeValidationDataModel>
GetValidationDataForVaultedToken(string paymentMethodToken)
{
Result<PaymentMethodNonce> result = await
_braintreeGateway.PaymentMethodNonce.CreateAsync(paymentMethodToken);
PaymentMethodNonce paymentMethodNonce =
result.Target;
BraintreeValidationDataModel validationData = new
BraintreeValidationDataModel
{
Nonce = paymentMethodNonce.Nonce,
Bin = paymentMethodNonce.Details.Bin
};
return validationData;
}
接下来,必须在客户端使用此信息来生成一个对3D安全支付有效的nonce。要实例化Braintree组件:
function setupComponents(clientToken) {
return Promise.all([
braintree.threeDSecure.create({
authorization: clientToken,
version: 2
}),
braintree.dataCollector.create({
authorization: clientToken
})
]);
}
生成3D安全nonce:
return setupComponents(threeDsValidation.clientToken).then(function (result) {
var threeDSecure =
result[0];
if (threeDSecure === null ||
threeDSecure === undefined) {
return null;
}
var dataCollector =
result[1];
if (dataCollector === null ||
dataCollector === undefined ||
dataCollector.deviceData == null) {
return null;
}
threeDsValidationNew.deviceData =
dataCollector.deviceData;
var threeDSecureParameters =
getThreeDsSecureParameters(threeDsValidationNew);
return threeDSecure.verifyCard(threeDSecureParameters);
})
.then(function (verifyCardResult) {
if (verifyCardResult === null ||
verifyCardResult === undefined ||
verifyCardResult.nonce == null) {
return null;
}
threeDsValidationNew.nonceSecure =
verifyCardResult.nonce;
return threeDsValidationNew;
});
其中getThreeDsSecureParameters为:
function getThreeDsSecureParameters(threeDsValidation) {
return {
amount: threeDsValidation.amount.toString(),
nonce: threeDsValidation.paymentMethodNonce,
bin: threeDsValidation.bin,
email: threeDsValidation.braintreeCharge.email,
billingAddress: {
givenName: threeDsValidation.braintreeCharge.billingName,
phoneNumber: threeDsValidation.braintreeCharge.phone,
streetAddress: threeDsValidation.braintreeCharge.streetAddress,
extendedAddress: threeDsValidation.braintreeCharge.extendedAddress,
locality: threeDsValidation.braintreeCharge.city,
region: threeDsValidation.braintreeCharge.state,
postalCode: threeDsValidation.braintreeCharge.zip,
countryCodeAlpha2: threeDsValidation.braintreeCharge.country
},
additionalInformation: {
shippingGivenName: threeDsValidation.braintreeCharge.shippingName,
shippingPhone: threeDsValidation.braintreeCharge.shippingPhone,
shippingAddress: {
streetAddress: threeDsValidation.braintreeCharge.shippingStreetAddress,
extendedAddress: threeDsValidation.braintreeCharge.shippingExtendedAddress,
locality: threeDsValidation.braintreeCharge.shippingCity,
region: threeDsValidation.braintreeCharge.shippingState,
postalCode: threeDsValidation.braintreeCharge.shippingZip,
countryCodeAlpha2: threeDsValidation.braintreeCharge.shippingCountry
}
},
onLookupComplete: function (data, next) {
next();
}
};
}
verifyCard返回的安全nonce可以在服务器端用作PaymentMethodNonce来完成3D安全交易。一个额外的DeviceData字段可以添加到服务器端的TransactionRequest中,它等于在setupComponents方法中创建的dataCollector对象的DeviceData字段。
TransactionRequest transactionRequest = new TransactionRequest
{
Amount = Amount,
PaymentMethodNonce = PaymentMethodNonce,
DeviceData = string.IsNullOrWhiteSpace(DeviceData) ? null : DeviceData,
MerchantAccountId = string.IsNullOrWhiteSpace(MerchantAccountId) ? null : MerchantAccountId,
TransactionSource = string.IsNullOrWhiteSpace(TransactionSource) ? null : TransactionSource,
CustomerId = string.IsNullOrWhiteSpace(CustomerId) ? null : CustomerId,
Options = new TransactionOptionsRequest
{
SubmitForSettlement = true,
ThreeDSecure = new TransactionOptionsThreeDSecureRequest
{
Required = chargeOptions.UseThreeDSecure
}
}
};
if (Address != null)
{
transactionRequest.BillingAddress = new AddressRequest
{
Company = string.IsNullOrWhiteSpace(Address.CompanyName) ? null : Address.CompanyName,
FirstName = string.IsNullOrWhiteSpace(Address.BillingName) ? null : Address.BillingName,
StreetAddress = string.IsNullOrWhiteSpace(Address.StreetAddress) ? null : Address.StreetAddress,
ExtendedAddress = string.IsNullOrWhiteSpace(Address.ExtendedAddress) ? null : Address.ExtendedAddress,
Locality = string.IsNullOrWhiteSpace(Address.City) ? null : Address.City,
Region = string.IsNullOrWhiteSpace(Address.State) ? null : Address.State,
PostalCode = string.IsNullOrWhiteSpace(Address.Zip) ? null : Address.Zip,
CountryCodeAlpha2 = string.IsNullOrWhiteSpace(Address.Country) ? null : Address.Country
};
}
IBraintreeChargeReturnModel result = await Pay(transactionRequest,
braintreeGateway,
chargeOptions.UseThreeDSecure);
请注意,当与Braintree生产帐户一起使用时,客户端代码可能显示一个窗口,要求客户完成额外的验证。