diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c6dafd6..0b18b1ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.1.0 +- Add `InstallmentCount` to `DisputeTransaction` on dispute webhooks +- Add `ImplicitlyVaultedPaymentMethodToken` and `ImplicitlyVaultedPaymentMethodGlobalId` to `PayPalDetails` + ## 5.0.0 - Add `RequestTimeoutException` and `GatewayTimeoutException` - Add `RISK_THRESHOLD` to GatewayRejectionReason constants diff --git a/src/Braintree/AndroidPayCard.cs b/src/Braintree/AndroidPayCard.cs index c0dbab82..a76c3188 100644 --- a/src/Braintree/AndroidPayCard.cs +++ b/src/Braintree/AndroidPayCard.cs @@ -2,6 +2,7 @@ namespace Braintree { + // NEXT_MAJOR_VERSION Rename Android Pay to Google Pay public class AndroidPayCard : PaymentMethod { public virtual string CardType { get; protected set; } diff --git a/src/Braintree/Braintree.csproj b/src/Braintree/Braintree.csproj index 3f97a676..41dde01a 100644 --- a/src/Braintree/Braintree.csproj +++ b/src/Braintree/Braintree.csproj @@ -4,7 +4,7 @@ Braintree Client Library Copyright © Braintree, a division of PayPal, Inc. 2020 - 5.0.0 + 5.1.0 Braintree net452;netstandard2.0 @@ -12,86 +12,8 @@ Braintree braintree;paypal;venmo;intenational;payments;gateway;currencies;money;visa;mastercard;bitcoin;maestro;apple pay;android pay;amex;jcb;diners club;discover;american express - - Add `RequestTimeoutException` and `GatewayTimeoutException` - - Add `RISK_THRESHOLD` to GatewayRejectionReason constants - - Add `processedWithNetworkToken` to `Transaction` - - Add `isNetworkTokenized` to `CreditCard` - Breaking changes: - - Drop support for .NET Core versions less than 2.1 (v1.0, v1.3, v2.0, etc) - - Drop .NET Standard 1.3 from `TargetFrameworks` - - Remove deprecated `TransparentRedirect`, `iDEAL`, `Coinbase`, and `IbanAccount` classes - - Remove deprecated SEPA mandate and Europe Bank Account error codes, test payment method nonces, and payment intrument types - - Remove `ForwardedComments` from `Dispute` - - Remove `PayPalVaultWithoutUpgrade` from `PaymentMethodRequest` - - Remove deprecated validation error codes: - - `SEPA_BANK_ACCOUNT_ACCOUNT_HOLDER_NAME_IS_REQUIRED` - - `SEPA_BANK_ACCOUNT_BIC_IS_REQUIRED` - - `SEPA_BANK_ACCOUNT_IBAN_IS_REQUIRED` - - `SEPA_MANDATE_ACCOUNT_HOLDER_NAME_IS_REQUIRED` - - `SEPA_MANDATE_BIC_INVALID_CHARACTER` - - `SEPA_MANDATE_BIC_IS_REQUIRED` - - `SEPA_MANDATE_BIC_LENGTH_IS_INVALID` - - `SEPA_MANDATE_BIC_UNSUPPORTED_COUNTRY` - - `SEPA_MANDATE_BILLING_ADDRESS_CONFLICT` - - `SEPA_MANDATE_BILLING_ADDRESS_ID_IS_INVALID` - - `SEPA_MANDATE_IBAN_INVALID_CHARACTER` - - `SEPA_MANDATE_IBAN_INVALID_FORMAT` - - `SEPA_MANDATE_IBAN_IS_REQUIRED` - - `SEPA_MANDATE_IBAN_UNSUPPORTED_COUNTRY` - - `SEPA_MANDATE_LOCALE_IS_UNSUPPORTED` - - `SEPA_MANDATE_TYPE_IS_REQUIRED` - - `TRANSACTION_AMOUNT_DOES_NOT_MATCH_IDEAL_PAYMENT_AMOUNT` - - `TRANSACTION_IDEAL_PAYMENT_NOT_COMPLETE` - - `TRANSACTION_IDEAL_PAYMENTS_CANNOT_BE_VAULTED` - - `TRANSACTION_MERCHANT_ACCOUNT_DOES_NOT_MATCH_IDEAL_PAYMENT_MERCHANT_ACCOUNT` - - `TRANSACTION_ORDER_ID_DOES_NOT_MATCH_IDEAL_PAYMENT_ORDER_ID` - - `TRANSACTION_ORDER_ID_IS_REQUIRED_WITH_IDEAL_PAYMENT` - - Remove deprecated webhook kinds: - - `GRANTED_PAYMENT_INSTRUMENT_UPDATE` - - `IDEAL_PAYMENT_COMPLETE` - - `IDEAL_PAYMENT_FAILED` - - Rename `DownForMaintenanceException` to `ServiceUnavailableException` - - Transaction searches and Transaction Line Items FindAll calls throw `UnexpectedException` instead of `DownForMaintenance` when search response yields unexpected results - - Remove `UseStaticHttpClient` configuration option for .NET Core integrations - - Remove `MasterpassCard` payment method - - Remove `AmexExpressCheckoutCard` payment method - - Bump API version to support declined refund objects - - Convert custom `Enumeration` subclasses to C# `Enum` types (fixes #86): - - `CreditCardCardType` - - `CreditCardCommercial` - - `CreditCardCustomerLocation` - - `CreditCardDebit` - - `CreditCardDurbinRegulated` - - `CreditCardHealthcare` - - `CreditCardPayroll` - - `CreditCardPrepaid` - - `DisbursementType` - - `DisputeKind` - - `DisputeReason` - - `DisputeStatus` - - `DocumentUploadKind` - - `FundingDestination` - - `IndustryDataAdditionalChargeKind` - - `MerchantAccountStatus` - - `PaymentInstrumentType` - - `PlanDurationUnit` - - `ProcessorResponseType` - - `ShippingMethod` - - `SubscriptionDurationUnit` - - `SubscriptionSource` - - `SubscriptionStatus` - - `TransactionCreatedUsing` - - `TransactionEscrowStatus` - - `TransactionGatewayRejectionReason` - - `TransactionIndustryType` - - `TransactionLineItemKind` - - `TransactionSource` - - `TransactionStatus` - - `TransactionType` - - `UsBankAccountVerificationMethod` - - `UsBankAccountVerificationStatus` - - `VerificationStatus` - - `WebhookKind` + - Add `InstallmentCount` to `DisputeTransaction` on dispute webhooks + - Add `ImplicitlyVaultedPaymentMethodToken` and `ImplicitlyVaultedPaymentMethodGlobalId` to `PayPalDetails` https://github.com/braintree/braintree_dotnet false diff --git a/src/Braintree/Customer.cs b/src/Braintree/Customer.cs index 6094672a..bd6c7d7d 100644 --- a/src/Braintree/Customer.cs +++ b/src/Braintree/Customer.cs @@ -31,6 +31,7 @@ public class Customer public virtual CreditCard[] CreditCards { get; protected set; } public virtual PayPalAccount[] PayPalAccounts { get; protected set; } public virtual ApplePayCard[] ApplePayCards { get; protected set; } + // NEXT_MAJOR_VERSION Rename Android Pay to Google Pay public virtual AndroidPayCard[] AndroidPayCards { get; protected set; } public virtual VenmoAccount[] VenmoAccounts { get; protected set; } public virtual VisaCheckoutCard[] VisaCheckoutCards { get; protected set; } @@ -90,6 +91,7 @@ protected internal Customer(NodeWrapper node, IBraintreeGateway gateway) ApplePayCards[i] = new ApplePayCard(applePayXmlNodes[i], gateway); } + // NEXT_MAJOR_VERSION Rename Android Pay to Google Pay var androidPayCardXmlNodes = node.GetList("android-pay-cards/android-pay-card"); AndroidPayCards = new AndroidPayCard[androidPayCardXmlNodes.Count]; for (int i = 0; i < androidPayCardXmlNodes.Count; i++) diff --git a/src/Braintree/DisputeTransaction.cs b/src/Braintree/DisputeTransaction.cs index 0380f6bb..dad46c46 100644 --- a/src/Braintree/DisputeTransaction.cs +++ b/src/Braintree/DisputeTransaction.cs @@ -7,6 +7,7 @@ public class DisputeTransaction public virtual decimal? Amount { get; protected set; } public virtual DateTime? CreatedAt { get; protected set; } public virtual string Id { get; protected set; } + public virtual int? InstallmentCount { get; protected set; } public virtual string OrderId { get; protected set; } public virtual string PaymentInstrumentSubtype { get; protected set; } public virtual string PurchaseOrderNumber { get; protected set; } @@ -16,6 +17,7 @@ protected internal DisputeTransaction(NodeWrapper node) Amount = node.GetDecimal("amount"); CreatedAt = node.GetDateTime("created-at"); Id = node.GetString("id"); + InstallmentCount = node.GetInteger("installment-count"); OrderId = node.GetString("order-id"); PaymentInstrumentSubtype = node.GetString("payment-instrument-subtype"); PurchaseOrderNumber = node.GetString("purchase-order-number"); diff --git a/src/Braintree/PayPalDetails.cs b/src/Braintree/PayPalDetails.cs index bd0239f5..835e4d5e 100644 --- a/src/Braintree/PayPalDetails.cs +++ b/src/Braintree/PayPalDetails.cs @@ -25,6 +25,8 @@ public class PayPalDetails public virtual string RefundFromTransactionFeeAmount { get; protected set; } public virtual string RefundFromTransactionFeeCurrencyIsoCode { get; protected set; } public virtual string Description { get; protected set; } + public virtual string ImplicitlyVaultedPaymentMethodToken { get; protected set; } + public virtual string ImplicitlyVaultedPaymentMethodGlobalId { get; protected set; } protected internal PayPalDetails(NodeWrapper node) { @@ -49,6 +51,8 @@ protected internal PayPalDetails(NodeWrapper node) RefundFromTransactionFeeAmount = node.GetString("refund-from-transaction-fee-amount"); RefundFromTransactionFeeCurrencyIsoCode = node.GetString("refund-from-transaction-fee-currency-iso-code"); Description = node.GetString("description"); + ImplicitlyVaultedPaymentMethodToken = node.GetString("implicitly-vaulted-payment-method-token"); + ImplicitlyVaultedPaymentMethodGlobalId = node.GetString("implicitly-vaulted-payment-method-global-id"); } [Obsolete("Mock Use Only")] diff --git a/src/Braintree/Properties/AssemblyInfo.cs b/src/Braintree/Properties/AssemblyInfo.cs index ce612b79..0799a301 100644 --- a/src/Braintree/Properties/AssemblyInfo.cs +++ b/src/Braintree/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Build Number // Revision // -[assembly: AssemblyVersion("5.0.0.0")] -[assembly: AssemblyFileVersion("5.0.0.0")] +[assembly: AssemblyVersion("5.1.0.0")] +[assembly: AssemblyFileVersion("5.1.0.0")] diff --git a/src/Braintree/TransactionAndroidPayCardRequest.cs b/src/Braintree/TransactionAndroidPayCardRequest.cs new file mode 100644 index 00000000..36d284e6 --- /dev/null +++ b/src/Braintree/TransactionAndroidPayCardRequest.cs @@ -0,0 +1,40 @@ +#pragma warning disable 1591 + +namespace Braintree +{ + // NEXT_MAJOR_VERSION Rename Android Pay to Google Pay + public class TransactionAndroidPayCardRequest : Request + { + public string Cryptogram { get; set; } + public string EciIndicator { get; set; } + public string ExpirationMonth { get; set; } + public string ExpirationYear { get; set; } + public string GoogleTransactionId { get; set; } + public string Number { get; set; } + public string SourceCardLastFour { get; set; } + public string SourceCardType { get; set; } + + public override string ToXml() + { + return ToXml("android-pay-card"); + } + + public override string ToXml(string rootElement) + { + return BuildRequest(rootElement).ToXml(); + } + + protected virtual RequestBuilder BuildRequest(string root) + { + return new RequestBuilder(root). + AddElement("cryptogram", Cryptogram). + AddElement("eci-indicator", EciIndicator). + AddElement("expiration-month", ExpirationMonth). + AddElement("expiration-year", ExpirationYear). + AddElement("number", Number). + AddElement("google-transaction-id", GoogleTransactionId). + AddElement("source-card-last-four", SourceCardLastFour). + AddElement("source-card-type", SourceCardType); + } + } +} diff --git a/src/Braintree/TransactionApplePayCardRequest.cs b/src/Braintree/TransactionApplePayCardRequest.cs new file mode 100644 index 00000000..16aad3af --- /dev/null +++ b/src/Braintree/TransactionApplePayCardRequest.cs @@ -0,0 +1,34 @@ +using System; +namespace Braintree +{ + public class TransactionApplePayCardRequest : Request + { + public string Number { get; set; } + public string CardholderName { get; set; } + public string Cryptogram { get; set; } + public string ExpirationMonth { get; set; } + public string ExpirationYear { get; set; } + public string EciIndicator { get; set; } + + public override string ToXml() + { + return ToXml("apple-pay-card"); + } + + public override string ToXml(string root) + { + return BuildRequest(root).ToXml(); + } + + protected virtual RequestBuilder BuildRequest(string root) + { + return new RequestBuilder(root). + AddElement("number", Number). + AddElement("cardholder-name", CardholderName). + AddElement("cryptogram", Cryptogram). + AddElement("expiration-month", ExpirationMonth). + AddElement("expiration-year", ExpirationYear). + AddElement("eci-indicator", EciIndicator); + } + } +} \ No newline at end of file diff --git a/src/Braintree/TransactionRequest.cs b/src/Braintree/TransactionRequest.cs index 4dc5aada..660452bc 100644 --- a/src/Braintree/TransactionRequest.cs +++ b/src/Braintree/TransactionRequest.cs @@ -87,6 +87,9 @@ public string ThreeDSecureToken { public string ShipsFromPostalCode { get; set; } public TransactionLineItemRequest[] LineItems { get; set; } public ExternalVaultRequest ExternalVault { get; set; } + // NEXT_MAJOR_VERSION Rename Android Pay to Google Pay + public TransactionAndroidPayCardRequest AndroidPayCard { get; set; } + public TransactionApplePayCardRequest ApplePayCard { get; set; } public TransactionRequest() { @@ -171,6 +174,10 @@ protected virtual RequestBuilder BuildRequest(string root) builder.AddElement("line-items", LineItems); if (ExternalVault != null) builder.AddElement("external-vault", ExternalVault); + if (AndroidPayCard != null) + builder.AddElement("android-pay-card", AndroidPayCard); + if (ApplePayCard != null) + builder.AddElement("apple-pay-card", ApplePayCard); return builder; } } diff --git a/test/Braintree.Tests.Integration/TransactionIntegrationTest.cs b/test/Braintree.Tests.Integration/TransactionIntegrationTest.cs index 9bf85dc6..8feb305f 100644 --- a/test/Braintree.Tests.Integration/TransactionIntegrationTest.cs +++ b/test/Braintree.Tests.Integration/TransactionIntegrationTest.cs @@ -3189,6 +3189,31 @@ public void Sale_WithApplePayNonce() Assert.IsNotNull(result.Target.ApplePayDetails.Bin); } + [Test] + public void Sale_WithApplePayParams() + { + TransactionRequest request = new TransactionRequest + { + Amount = SandboxValues.TransactionAmount.AUTHORIZE, + ApplePayCard = new TransactionApplePayCardRequest + { + Number = "4111111111111111", + CardholderName = "Dan Schulman", + Cryptogram = "AAAAAAAA/COBt84dnIEcwAA3gAAGhgEDoLABAAhAgAABAAAALnNCLw==", + ExpirationMonth = "05", + ExpirationYear = "10", + EciIndicator = "07" + } + }; + Result result = gateway.Transaction.Sale(request); + Assert.IsTrue(result.IsSuccess()); + + Assert.IsNotNull(result.Target.ApplePayDetails); + Assert.AreEqual("Dan Schulman", result.Target.ApplePayDetails.CardholderName); + Assert.AreEqual("05", result.Target.ApplePayDetails.ExpirationMonth); + Assert.AreEqual("2010", result.Target.ApplePayDetails.ExpirationYear); + } + [Test] public void Sale_WithAndroidPayProxyCardNonce() { @@ -3260,6 +3285,40 @@ public void Sale_WithAndroidPayNetworkTokenNonce() Assert.IsFalse(androidPayDetails.IsNetworkTokenized); } + [Test] + public void Sale_WithGooglePayParams() + { + TransactionRequest request = new TransactionRequest + { + Amount = SandboxValues.TransactionAmount.AUTHORIZE, + AndroidPayCard = new TransactionAndroidPayCardRequest + { + Cryptogram = "AAAAAAAA/COBt84dnIEcwAA3gAAGhgEDoLABAAhAgAABAAAALnNCLw==", + EciIndicator = "05", + ExpirationMonth = "10", + ExpirationYear = "14", + GoogleTransactionId = "25469d622c1dd37cb1a403c6d438e850", + Number = "4012888888881881", + SourceCardLastFour = "1881", + SourceCardType = "Visa" + } + }; + Result result = gateway.Transaction.Sale(request); + Assert.IsTrue(result.IsSuccess()); + Assert.AreEqual(PaymentInstrumentType.ANDROID_PAY_CARD, result.Target.PaymentInstrumentType); + + Assert.IsNotNull(result.Target.AndroidPayDetails); + + Assert.IsInstanceOf(typeof(AndroidPayDetails), result.Target.AndroidPayDetails); + AndroidPayDetails androidPayDetails = result.Target.AndroidPayDetails; + + Assert.AreEqual("Visa", androidPayDetails.SourceCardType); + Assert.AreEqual("1881", androidPayDetails.SourceCardLast4); + Assert.AreEqual("10", androidPayDetails.ExpirationMonth); + Assert.AreEqual("14", androidPayDetails.ExpirationYear); + Assert.AreEqual("25469d622c1dd37cb1a403c6d438e850", androidPayDetails.GoogleTransactionId); + } + [Test] public void Sale_WithVenmoAccountNonce() { diff --git a/test/Braintree.Tests/DisputeTest.cs b/test/Braintree.Tests/DisputeTest.cs index 67411b42..ebfaba0d 100644 --- a/test/Braintree.Tests/DisputeTest.cs +++ b/test/Braintree.Tests/DisputeTest.cs @@ -99,6 +99,7 @@ public string Payload_attributes() Node("id", "new_transaction_id"), Node("amount", "101.00"), NodeAttr("created-at", TYPE_DATE, "2017-06-21T20:44:41Z"), + NodeAttr("installment-count", NIL_TRUE), NodeAttr("order-id", NIL_TRUE), NodeAttr("purchase-order-number", NIL_TRUE), Node("payment-instrument-subtype", "Visa") @@ -132,6 +133,7 @@ public string Payload_attributesWithEmptyValues() Node("id", "new_transaction_id"), Node("amount", "101.00"), NodeAttr("created-at", TYPE_DATE, "2017-06-21T20:44:41Z"), + NodeAttr("installment-count", NIL_TRUE), NodeAttr("order-id", NIL_TRUE), NodeAttr("purchase-order-number", NIL_TRUE), Node("payment-instrument-subtype", "Visa") @@ -245,6 +247,7 @@ public void Constructor_populatesTransaction() var result = new Dispute(node); Assert.AreEqual("new_transaction_id", result.Transaction.Id); Assert.AreEqual(101m, result.Transaction.Amount); + Assert.IsNull(result.Transaction.InstallmentCount); Assert.IsNull(result.Transaction.OrderId); Assert.AreEqual("Visa", result.Transaction.PaymentInstrumentSubtype); Assert.IsNull(result.Transaction.PurchaseOrderNumber); diff --git a/test/Braintree.Tests/PayPalDetailsTest.cs b/test/Braintree.Tests/PayPalDetailsTest.cs new file mode 100644 index 00000000..70637b97 --- /dev/null +++ b/test/Braintree.Tests/PayPalDetailsTest.cs @@ -0,0 +1,74 @@ +using System.Xml; +using NUnit.Framework; + +namespace Braintree.Tests +{ + [TestFixture] + public class PayPalDetailsTest + { + private NodeWrapper nodeFromXml(string xml) + { + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + XmlNode newNode = doc.DocumentElement; + return new NodeWrapper(newNode); + } + + [Test] + public void IncludesFields() + { + string xml = "" + + "abc@test.com" + + "1234567890" + + "12345" + + "token" + + "www.image-url.com" + + "12345" + + "6789" + + "payee@test.com" + + "so custom much wow" + + "1357" + + "Grace" + + "Hopper" + + "status" + + "12345" + + "8675309" + + "2468" + + "10.00" + + "123" + + "2.00" + + "123" + + "item" + + "implicittoken" + + "implicitglobalid" + + ""; + var node = nodeFromXml(xml); + + PayPalDetails details = new PayPalDetails(node); + + Assert.AreEqual("abc@test.com", details.PayerEmail); + Assert.AreEqual("1234567890", details.PaymentId); + Assert.AreEqual("12345", details.AuthorizationId); + Assert.AreEqual("token", details.Token); + Assert.AreEqual("www.image-url.com", details.ImageUrl); + Assert.AreEqual("12345", details.DebugId); + Assert.AreEqual("6789", details.PayeeId); + Assert.AreEqual("payee@test.com", details.PayeeEmail); + Assert.AreEqual("so custom much wow", details.CustomField); + Assert.AreEqual("1357", details.PayerId); + Assert.AreEqual("Grace", details.PayerFirstName); + Assert.AreEqual("Hopper", details.PayerLastName); + Assert.AreEqual("status", details.PayerStatus); + Assert.AreEqual("12345", details.SellerProtectionStatus); + Assert.AreEqual("8675309", details.RefundId); + Assert.AreEqual("2468", details.CaptureId); + Assert.AreEqual("10.00", details.TransactionFeeAmount); + Assert.AreEqual("123", details.TransactionFeeCurrencyIsoCode); + Assert.AreEqual("2.00", details.RefundFromTransactionFeeAmount); + Assert.AreEqual("123", details.RefundFromTransactionFeeCurrencyIsoCode); + Assert.AreEqual("item", details.Description); + Assert.AreEqual("implicittoken", details.ImplicitlyVaultedPaymentMethodToken); + Assert.AreEqual("implicitglobalid", details.ImplicitlyVaultedPaymentMethodGlobalId); + } + } +} \ No newline at end of file diff --git a/test/Braintree.Tests/TransactionAndroidPayCardTest.cs b/test/Braintree.Tests/TransactionAndroidPayCardTest.cs new file mode 100644 index 00000000..d9704b05 --- /dev/null +++ b/test/Braintree.Tests/TransactionAndroidPayCardTest.cs @@ -0,0 +1,33 @@ +using NUnit.Framework; + +namespace Braintree.Tests +{ + [TestFixture] + public class TransactionAndroidPayCardTest + { + [Test] + public void ToXml_IncludesAllProperties() + { + TransactionAndroidPayCardRequest request = new TransactionAndroidPayCardRequest + { + Cryptogram = "some-cryptogram", + EciIndicator = "some-eci-indicator", + ExpirationMonth = "some-month", + ExpirationYear = "some-year", + GoogleTransactionId = "some-id", + Number = "some-number", + SourceCardLastFour = "some-last-four", + SourceCardType = "some-card-type" + }; + + StringAssert.Contains("some-cryptogram", request.ToXml()); + StringAssert.Contains("some-eci-indicator", request.ToXml()); + StringAssert.Contains("some-month", request.ToXml()); + StringAssert.Contains("some-year", request.ToXml()); + StringAssert.Contains("some-id", request.ToXml()); + StringAssert.Contains("some-number", request.ToXml()); + StringAssert.Contains("some-last-four", request.ToXml()); + StringAssert.Contains("some-card-type", request.ToXml()); + } + } +} diff --git a/test/Braintree.Tests/TransactionApplePayCardRequestTest.cs b/test/Braintree.Tests/TransactionApplePayCardRequestTest.cs new file mode 100644 index 00000000..f6ea5369 --- /dev/null +++ b/test/Braintree.Tests/TransactionApplePayCardRequestTest.cs @@ -0,0 +1,29 @@ +using NUnit.Framework; + +namespace Braintree.Tests +{ + [TestFixture] + public class TransactionApplePayCardRequestTest + { + [Test] + public void ToXml_IncludesAllProperties() + { + var request = new TransactionApplePayCardRequest() + { + Number = "4111111111111111", + CardholderName = "Dan Schulman", + Cryptogram = "abc123", + ExpirationMonth = "01", + ExpirationYear = "19", + EciIndicator = "07" + }; + + Assert.IsTrue(request.ToXml().Contains("4111111111111111")); + Assert.IsTrue(request.ToXml().Contains("Dan Schulman")); + Assert.IsTrue(request.ToXml().Contains("abc123")); + Assert.IsTrue(request.ToXml().Contains("01")); + Assert.IsTrue(request.ToXml().Contains("19")); + Assert.IsTrue(request.ToXml().Contains("07")); + } + } +}