简体   繁体   中英

Is it possible to make 3D Secure payments through a Braintree sandbox environment with C#?

I have written code in .NET Standard 2.0 to make payments through Braintree. The code uses the Braintree 5.2.0 NuGet package. I intend to exclusively make 3D Secure payments when the code is used against a Braintree production account. I have written a integration test that creates a customer, creates a payment method for that customer, then makes a payment against that payment method using the Token that was generated.

The code to create a customer is:

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;
}

The code to create a payment method is:

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);

CreditCard.Token is mapped to AddPaymentMethodResultModel.CardId.

The code to make a payment is:

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);

When I execute the test against a Braintree sandbox environment with TransactionRequest.Options.ThreeDSecure.Required set to false, a payment is successful.

When I execute the test against a Braintree sandbox environment TransactionRequest.Options.ThreeDSecure.Required set to true, a payment fails with a Result.Message of Gateway Rejected: three_d_secure .

I was wondering whether it is possible to make a successful payment through a Braintree sandbox environment with TransactionRequest.Options.ThreeDSecure.Required set to true. I have tried unsuccessfully using a payment method created through the NuGet package with a PaymentMethodNonce of fake-three-d-secure-visa-full-authentication-nonce .

I would like to prove that a 3D Secure payment can be made through a Braintree sandbox environment to have confidence that the code will work against a Braintree production environment.

There is a client-side piece that must be implemented to validate a vaulted card before it can be used to make a 3D Secure payment.

There are two approaches that can be taken to achieve this: drop-in UI or hosted fields . Drop-in UI provides a form with some styling, while hosted field provides more control over the appearance of your form. I did not require the fields to be visible, as I could manage the data through JavaScript alone, so I used hosted fields.

First, one must generate a client token, as well as a nonce and bin representing the vaulted card on the server-side. The code for achieving this was:

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;
}

Next, one must use this information on the client-side to generate a nonce that is valid for a 3D Secure payment. To instantiate the Braintree components:

function setupComponents(clientToken) {
    return Promise.all([
        braintree.threeDSecure.create({
            authorization: clientToken,
            version: 2
        }),
        braintree.dataCollector.create({
            authorization: clientToken
        })
    ]);
}

To generate a 3D Secure 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;
});

Where getThreeDsSecureParameters is:

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();
        }
    };
}

The secure nonce returned by verifyCard can then be used as the PaymentMethodNonce on the server side to complete a 3D Secure transaction. An additional field of DeviceData can be added to the TransactionRequest on the server-side, which is equal to the deviceData field of the dataCollector object that was created in the setupComponents method.

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);

Be aware that the client-side code may display a window asking for the customer to complete additional validation when used with a production Braintree account.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM