OrderCloud.Integrations.Payment.Stripe 1.0.1-alpha

This is a prerelease version of OrderCloud.Integrations.Payment.Stripe.
There is a newer version of this package available.
See the version list below for details.
dotnet add package OrderCloud.Integrations.Payment.Stripe --version 1.0.1-alpha
NuGet\Install-Package OrderCloud.Integrations.Payment.Stripe -Version 1.0.1-alpha
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OrderCloud.Integrations.Payment.Stripe" Version="1.0.1-alpha" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add OrderCloud.Integrations.Payment.Stripe --version 1.0.1-alpha
#r "nuget: OrderCloud.Integrations.Payment.Stripe, 1.0.1-alpha"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install OrderCloud.Integrations.Payment.Stripe as a Cake Addin
#addin nuget:?package=OrderCloud.Integrations.Payment.Stripe&version=1.0.1-alpha&prerelease

// Install OrderCloud.Integrations.Payment.Stripe as a Cake Tool
#tool nuget:?package=OrderCloud.Integrations.Payment.Stripe&version=1.0.1-alpha&prerelease

OrderCloud.Integrations.Payment.Stripe

This project brings payment processing to your ecommerce app using the Stripe API. It will be published as a nuget code library and conforms to the standard interfaces ICreditCardProcessor and ICreditCardSaver published in the base library ordercloud-dotnet-catalyst.

Payment Processing Basics

The OrderCloud API does not have integration points designed specifically for payment proccesing events. Yet secure payments are critical to every Ecommerce solution. Here we will share best practices for payments which are important to understand before using this library.

  • As an online retailer, your payments technology needs to be PCI compliant. This will be a much simpler process if you correctly use a payment gateway provider like Stripe. Correct use means that your UI for entering card details is an Iframe owned by the payment gateway and that you never save or transmit full card numbers or CVVs. In other words, the code you write should only handle tokens or IDs representing card numbers. These tokens should be generated by the payment gateway's Iframe. Do not put full card numbers in OrderCloud!
  • The same rules apply to a user's saved cards. They should be vaulted in the processor's system, and only the Token stored in OrderCloud or your database.
  • Once the user enters their payment details and you've seccurely tokenized it, what happens next? For credit cards, you have 2 choices. You can authorize the card - which verifies funds are available and holds them, but is easy to reverse with a void. Or you can authorize and capture - which begins the transfer of funds immediately, but can only be reversed with a refund (which will cost you, the merchant, a processing fee). Incorrectly placed, canceled, or refunded orders are very common in Ecommerce, so it is strongly recommended that you wait to Capture.
  • Both the authorization request and the capture request must be made from a secure server-side context. Authorization should take place only after a user has indicated they want to submit the order, but it must succeed before an Order's status should be set to submitted. A pre-webhook endpoint or a proxy endpoint for order submit are the best ways to achieve this.
  • There are different valid options for when to Capture, including when the order is shipped, a set time period after the order is placed, or in a nightly batch job.
  • If you need to reverse a transaction because of a cancelation or refund, you either Void the transaction or Refund, depending on if capture has taken place.

Table of key credit card events

Card details are collected via Stripe.js in the client. Once details are collected, a Payment Method will need to be created either in the client or in your hosted server. Once a Payment Method is created, you will use that ID in your request body to ICreditCardProcessor.AuthorizeOnlyAsync().

Description Integration Method Stripe Documentation OrderCloud Platform Context
Request an authnetication cred for the IFrame ICreditCardProcessor.GetIFrameCredentialAsync() Link Stripe publishable key is set in a config and forwarded to the front end.
Tokenize the user's card details with Stripe Payment Element None Link Save the token and PCI-safe card details (last 4 digits) on a Payment object attached to the Order
Verify and hold funds ICreditCardProcessor.AuthorizeOnlyAsync() Link Within a pre-webhook or proxy route list Payments, attempt to authorize using the token, set payment accepted true, create a payment transaction, and then submit the Order
Cancel or refund before capture ICreditCardProcessor.VoidAuthorizationAsync() Link In response to a cancelation, void server-side and create a payment transaction.
Capture funds ICreditCardProcessor.CapturePriorAuthorizationAsync() Link Catpure when the order is shipped or during a nightly batch job. Create a payment transaction.
Cancel or refund after capture ICreditCardProcessor.RefundCaptureAsync() Link In response to a cancelation, refund server-side and create a payment transaction.

Package Installation

This nuget library can be installed in the context of a .NET server-side project. If you already have a .NET project, great. If not, you can follow this guide.

dotnet add package OrderCloud.Integrations.Payment.Stripe

Authentication and Injection

You will need a SecretKey configured to authneticate to the Stripe API. Click here to learn more about Stripe's API keys.

var stripeService = new StripeService(new StripeConfig()
{
	SecretKey = "sk_123456...."
});

For efficient use of compute resources and clean code, create 1 StripeService object and make it available throughout your project using inversion of control dependency injection.

services.AddSingleton<ICreditCardProcessor>(stripeService);
services.AddSingleton<ICreditCardSaver>(stripeService);

Notice that ICreditCardProcessor and ICreditCardSaver are not specific to Stripe. They are general to the problem domain and come from the upstream ordercloud-dotnet-catalyst package.

Usage

Inject the interfaces and use them within route logic. Rely on the interfaces whenever you can, not StripeService. The layer of abstraction that ICreditCardProcessor and ICreditCardSaver provide decouples your code from Stripe as a specific provider and hides some internal complexity.

public class CreditCardCommand 
{
	private readonly ICreditCardProcessor _creditCardProcessor;
	private readonly ICreditCardSaver _creditCardSaver;

	public CheckoutIntegrationEventController(ICreditCardProcessor creditCardProcessor, ICreditCardSaver creditCardSaver)
	{
		// Inject interface. Implementation will depend on how services were registered, StripeService in this case.
		_creditCardProcessor = shipMethodCalculator; 
		_creditCardSaver = creditCardSaver;
	}

	...

	// Use in pre-submit webhook or proxy route
	public async Task<PaymentWithXp> AuthorizeCardPayment(OrderWorksheetWithXp worksheet, PaymentWithXp payment)
	{
		var authorizeRequest = new AuthorizeCCTransaction()
		{
			OrderID = worksheet.Order.ID,
			Amount = worksheet.Order.Total,
			Currency = worksheet.Order.Currency,
			AddressVerification = worksheet.Order.BillingAddress,
			CustomerIPAddress = "...",
		};
		var payWithSavedCard = payment?.xp?.SafeCardDetails?.SavedCardID != null;
		if (payWithSavedCard)
		{
			authorizeRequest.SavedCardID = payment.xp.SafeCardDetails.SavedCardID;
			authorizeRequest.ProcessorCustomerID = worksheet.Order.FromUser.xp.PaymentProcessorCustomerID;
		}
		else
		{
			authorizeRequest.CardToken = payment?.xp?.SafeCardDetails?.Token;
		}

		CCTransactionResult authorizationResult = await _creditCardProcessor.AuthorizeOnlyAsync(authorizeRequest);

		Require.That(authorizationResult.Succeeded, new ErrorCode("Payment.AuthorizeDidNotSucceed", authorizationResult.Message), authorizationResult);

		await _oc.Payments.PatchAsync<PaymentWithXp>(OrderDirection.All, worksheet.Order.ID, payment.ID, new PartialPayment { Accepted = true, Amount = authorizeRequest.Amount });
		var updatedPayment = await _oc.Payments.CreateTransactionAsync<PaymentWithXp>(OrderDirection.All, worksheet.Order.ID, payment.ID, new PaymentTransactionWithXp()
		{
			ID = authorizationResult.TransactionID,
			Amount = payment.Amount,
			DateExecuted = DateTime.Now,
			ResultCode = authorizationResult.AuthorizationCode,
			ResultMessage = authorizationResult.Message,
			Succeeded = authorizationResult.Succeeded,
			Type = PaymentTransactionType.Authorization.ToString(),
			xp = new PaymentTransactionXp
			{
				TransactionDetails = authorizationResult,
			}
		});
		return updatedPayment;
	}
}

This library also supports more complex cases that require mulitple merchant accounts with different credentials. For example, in a franchise business model where each location is independent but all sell on one ecommerce solution. In that case, still inject one instance of StripeService exactly as above. You can provide empty strings for the credentials. However, when you call methods on the interfaces, provide the optional configOverride parameter.

StripeConfig configOverride = await FetchPaymentAccountCredentials(supplierID)
var authorize = new AuthorizeCCTransaction();
List<List<ShipMethods> rates = await _creditCardProcessor.AuthorizeOnlyAsynct(authorize, configOverride);
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.3.0 6,073 10/31/2022
1.0.3-alpha 181 5/27/2022
1.0.2-alpha 2,014 5/17/2022
1.0.1-alpha 150 5/17/2022