đź’ł Building a Lightweight PayPal Payment Gateway Service in C#

Introduction

Processing online payments is one of the core parts of modern web applications. Whether you’re selling subscriptions, digital goods, or services, integrating a secure and reusable payment gateway should be simple and clean.

In …


This content originally appeared on DEV Community and was authored by David Au Yeung

Introduction

Processing online payments is one of the core parts of modern web applications. Whether you're selling subscriptions, digital goods, or services, integrating a secure and reusable payment gateway should be simple and clean.

In this guide, you'll learn how to build a reusable PayPal Payment Service in C#, built for dependency injection, making it easy to plug into your ASP.NET Core apps.

We'll use the PayPal REST API, connecting to the Sandbox environment for safe testing.

Why a Service-Based Architecture?

In production apps, putting payment logic directly into controllers (or Program.cs) quickly becomes messy.

A service-based design makes it easy to:

  • Inject payment functionality anywhere
  • Reuse core logic across multiple controllers or jobs
  • Centralize configuration (e.g., PayPal credentials)
  • Unit test using mock interfaces

That's what we'll build next: a self-contained, secure IPayPalService implementation.

Prerequisites

You'll need:

  • A PayPal Developer Account https://developer.paypal.com
  • A Sandbox App with Client ID and Secret
  • .NET SDK (6.0 or later)
  • Newtonsoft.Json package

API Credentials:

Install dependencies:

dotnet add package Newtonsoft.Json

Step 1: Create the PayPal Service Interface

This defines what your service can do, keeping it easy to stub for testing.

public interface IPayPalService
{
    Task<string> CreatePaymentAsync(decimal amount, string currency, string description);
    Task<string> ExecutePaymentAsync(string paymentId, string payerId);
}

Step 2: Implement the PayPal Service

This class handles authentication, payment creation, and execution.

It's structured for reuse and injected HttpClient for proper connection management.

using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System.Net.Http.Headers;
using System.Text;

public class PayPalService : IPayPalService
{
    private readonly HttpClient _client;
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly string _baseUrl;

    public PayPalService(HttpClient client, IConfiguration config)
    {
        _client = client;
        _clientId = config["PayPal:ClientId"];
        _clientSecret = config["PayPal:ClientSecret"];
        _baseUrl = config.GetValue<string>("PayPal:BaseUrl") ?? "https://api.sandbox.paypal.com";
    }

    public async Task<string> CreatePaymentAsync(decimal amount, string currency, string description)
    {
        var token = await GetAccessTokenAsync();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var payment = new
        {
            intent = "sale",
            payer = new { payment_method = "paypal" },
            transactions = new[]
            {
                new { amount = new { total = amount.ToString("F2"), currency }, description }
            },
            redirect_urls = new
            {
                return_url = "https://example.com/success",
                cancel_url = "https://example.com/cancel"
            }
        };

        var json = JsonConvert.SerializeObject(payment);
        var response = await _client.PostAsync($"{_baseUrl}/v1/payments/payment",
            new StringContent(json, Encoding.UTF8, "application/json"));

        return await response.Content.ReadAsStringAsync();
    }

    public async Task<string> ExecutePaymentAsync(string paymentId, string payerId)
    {
        var token = await GetAccessTokenAsync();
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var body = new { payer_id = payerId };
        var json = JsonConvert.SerializeObject(body);

        var response = await _client.PostAsync($"{_baseUrl}/v1/payments/payment/{paymentId}/execute",
            new StringContent(json, Encoding.UTF8, "application/json"));

        return await response.Content.ReadAsStringAsync();
    }

    private async Task<string> GetAccessTokenAsync()
    {
        var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_clientId}:{_clientSecret}"));
        _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);

        var form = new StringContent("grant_type=client_credentials",
            Encoding.UTF8, "application/x-www-form-urlencoded");

        var response = await _client.PostAsync($"{_baseUrl}/v1/oauth2/token", form);
        var content = await response.Content.ReadAsStringAsync();

        if (!response.IsSuccessStatusCode)
            throw new Exception($"Failed to get token: {response.StatusCode} - {content}");

        var token = JsonConvert.DeserializeObject<TokenResponse>(content);
        return token.access_token;
    }

    private class TokenResponse
    {
        public string access_token { get; set; }
        public int expires_in { get; set; }
    }
}

Step 3: Configure the Service in Program.cs

Register the PayPalService and load its settings from configuration.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;

var builder = WebApplication.CreateBuilder(args);

// Add configuration
builder.Configuration.AddJsonFile("appsettings.json");

// Register HttpClient + PayPal service
builder.Services.AddHttpClient<IPayPalService, PayPalService>();

var app = builder.Build();

app.MapGet("/create-payment", async (IPayPalService payPalService) =>
{
    var result = await payPalService.CreatePaymentAsync(25.00m, "USD", "Test Product");
    return Results.Content(result, "application/json");
});

app.Run();

Example appsettings.json

{
  "PayPal": {
    "ClientId": "YOUR_PAYPAL_CLIENT_ID",
    "ClientSecret": "YOUR_PAYPAL_SECRET",
    "BaseUrl": "https://api.sandbox.paypal.com"
  }
}

Step 4: Try It in Sandbox

1) Run your app:

   dotnet run

2) Visit:

   http://localhost:5000/create-payment

3) You should see a PayPal JSON response similar to this:

{
  "id": "PAYID-NEKHSNA8G910153S62578812",
  "intent": "sale",
  "state": "created",
  "payer": {
    "payment_method": "paypal"
  },
  "transactions": [
    {
      "amount": {
        "total": "25.00",
        "currency": "USD"
      },
      "description": "Test Product",
      "related_resources": []
    }
  ],
  "create_time": "2025-11-12T12:10:28Z",
  "links": [
    {
      "href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-NEKHSNA8G910153S62578812",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-1KS533720P2426713",
      "rel": "approval_url",
      "method": "REDIRECT"
    },
    {
      "href": "https://api.sandbox.paypal.com/v1/payments/payment/PAYID-NEKHSNA8G910153S62578812/execute",
      "rel": "execute",
      "method": "POST"
    }
  ]
}

4) From this response, take note of:

  • id → The payment ID (PAYID-...) for later execution
  • approval_url → The URL where you redirect the user to confirm the payment
  • execute link → Used to finalize (capture) the payment after the user approves it

To test the flow, redirect your user to the approval URL. Once the buyer completes the transaction in the sandbox UI, PayPal will return them to your return_url along with the paymentId and PayerID parameters.

You can then use those values to call your /execute-payment endpoint.

Step 5: (Optional) Execute and Confirm the Payment

Once PayPal redirects users back to your site with a paymentId and PayerId, execute the payment:

app.MapPost("/execute-payment", async (string paymentId, string payerId, IPayPalService payPalService) =>
{
    var result = await payPalService.ExecutePaymentAsync(paymentId, payerId);
    return Results.Content(result, "application/json");
});

Why This Design is Good

Reusable: Inject IPayPalService anywhere: controllers, APIs, or background workers.

Clean Configuration: Credentials and base URL in appsettings.json.

Testable: Mock the interface in unit tests.

Scalable: Add more gateways (Stripe, Razorpay, etc.) behind different service implementations.

This structure follows SOLID and modern ASP.NET Core best practices.

Next Steps

  • Add webhook handling for automatic payment updates
  • Support subscriptions or invoicing
  • Use IOptions<PayPalOptions> for typed configuration
  • Switch to live mode when ready (`https://api.paypal.com)

Conclusion

By wrapping PayPal's REST API in a C# service with dependency injection, you create a flexible, maintainable payment gateway integration suitable for real-world apps.

This design simplifies future enhancements, improves testability, and allows clean extension to other providers.

Love C#!


This content originally appeared on DEV Community and was authored by David Au Yeung


Print Share Comment Cite Upload Translate Updates
APA

David Au Yeung | Sciencx (2025-11-12T13:10:42+00:00) đź’ł Building a Lightweight PayPal Payment Gateway Service in C#. Retrieved from https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/

MLA
" » đź’ł Building a Lightweight PayPal Payment Gateway Service in C#." David Au Yeung | Sciencx - Wednesday November 12, 2025, https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/
HARVARD
David Au Yeung | Sciencx Wednesday November 12, 2025 » đź’ł Building a Lightweight PayPal Payment Gateway Service in C#., viewed ,<https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/>
VANCOUVER
David Au Yeung | Sciencx - » đź’ł Building a Lightweight PayPal Payment Gateway Service in C#. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/
CHICAGO
" » đź’ł Building a Lightweight PayPal Payment Gateway Service in C#." David Au Yeung | Sciencx - Accessed . https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/
IEEE
" » đź’ł Building a Lightweight PayPal Payment Gateway Service in C#." David Au Yeung | Sciencx [Online]. Available: https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/. [Accessed: ]
rf:citation
» đź’ł Building a Lightweight PayPal Payment Gateway Service in C# | David Au Yeung | Sciencx | https://www.scien.cx/2025/11/12/%f0%9f%92%b3-building-a-lightweight-paypal-payment-gateway-service-in-c/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.