Server
C# / .NET SDK
Drop-in C# implementation for integrating YardPay Pro into .NET 6+ applications. Uses HttpClient and System.Text.Json — no NuGet packages required.
API Client
Create YardPayProClient.cs
// YardPayProClient.cs
using System.Net.Http.Json;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace YardPayPro;
public class YppException : Exception
{
public int Status { get; }
public string Code { get; }
public YppException(int status, string code, string message)
: base(message)
{
Status = status;
Code = code;
}
}
public class YardPayProClient : IDisposable
{
private readonly HttpClient _http;
private readonly string _baseUrl;
private static readonly JsonSerializerOptions JsonOpts = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
public YardPayProClient(string apiKey, string baseUrl = "")
{
_baseUrl = baseUrl;
_http = new HttpClient();
_http.DefaultRequestHeaders.Add("x-api-key", apiKey);
_http.Timeout = TimeSpan.FromSeconds(30);
}
public async Task<T?> RequestAsync<T>(HttpMethod method, string path,
object? body = null, Dictionary<string, string>? queryParams = null)
{
var url = _baseUrl + path;
if (queryParams is { Count: > 0 })
{
var qs = string.Join("&", queryParams
.Where(kv => kv.Value != null)
.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
url += "?" + qs;
}
using var request = new HttpRequestMessage(method, url);
if (body != null)
{
request.Content = JsonContent.Create(body, options: JsonOpts);
}
var response = await _http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
var errBody = await response.Content.ReadAsStringAsync();
try
{
var err = JsonSerializer.Deserialize<ApiError>(errBody, JsonOpts);
throw new YppException((int)response.StatusCode, err?.Code ?? "api_error", err?.Message ?? response.ReasonPhrase ?? "Unknown error");
}
catch (JsonException)
{
throw new YppException((int)response.StatusCode, "api_error", response.ReasonPhrase ?? "Unknown error");
}
}
if (response.StatusCode == System.Net.HttpStatusCode.NoContent)
return default;
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
// Handle OData { "value": [...] } wrapper
if (doc.RootElement.TryGetProperty("value", out var valueEl))
{
return JsonSerializer.Deserialize<T>(valueEl.GetRawText(), JsonOpts);
}
return JsonSerializer.Deserialize<T>(json, JsonOpts);
}
public Task<T?> GetAsync<T>(string path, Dictionary<string, string>? queryParams = null)
=> RequestAsync<T>(HttpMethod.Get, path, queryParams: queryParams);
public Task<T?> PostAsync<T>(string path, object body)
=> RequestAsync<T>(HttpMethod.Post, path, body);
public Task<T?> PutAsync<T>(string path, object body)
=> RequestAsync<T>(HttpMethod.Put, path, body);
public Task DeleteAsync(string path)
=> RequestAsync<object>(HttpMethod.Delete, path);
public void Dispose() => _http.Dispose();
private record ApiError(string? Code, string? Message);
}Models
Create Models.cs
// Models.cs
namespace YardPayPro;
public record PayInvoiceRequest
{
public int? InvoiceId { get; init; }
public string? InvoiceIdentifier { get; init; }
public int PaymentOptionId { get; init; }
public int? FundingSourceId { get; init; }
public CardInfo? Card { get; init; }
public BillingAddress? BillingAddress { get; init; }
}
public record CardInfo
{
public string Name { get; init; } = "";
public string CardNumber { get; init; } = "";
public string ExpiryDate { get; init; } = "";
public string Cvv { get; init; } = "";
}
public record BillingAddress
{
public string FirstName { get; init; } = "";
public string LastName { get; init; } = "";
public string AddressLine1 { get; init; } = "";
public string City { get; init; } = "";
public string Country { get; init; } = "";
}
public record PaymentResponse
{
public string? ReferenceNumber { get; init; }
public decimal? AmountPaid { get; init; }
public decimal? AmountDue { get; init; }
public string? Currency { get; init; }
public string? PaymentStatusId { get; init; }
public bool IsSuccess { get; init; }
public string? ApprovalAction { get; init; }
public string? ApprovalDataOrUrl { get; init; }
public string? HostMessage { get; init; }
}
public record PaymentMethodInfo
{
public int Id { get; init; }
public string Code { get; init; } = "";
public string Name { get; init; } = "";
public string PaymentMethodId { get; init; } = "";
public bool IsEnabled { get; init; }
public string? CurrencyCode { get; init; }
}
public record Invoice
{
public int Id { get; init; }
public string Identifier { get; init; } = "";
public string? Description { get; init; }
public string Status { get; init; } = "";
public decimal TotalCost { get; init; }
public string CurrencyCode { get; init; } = "";
public decimal? TotalDue { get; init; }
public decimal? TotalPaid { get; init; }
}
public record CreateInvoiceRequest
{
public string? Description { get; init; }
public string CurrencyCode { get; init; } = "JMD";
public string? PaymentDueDate { get; init; }
public List<InvoiceItemRequest> Items { get; init; } = new();
public CounterPartyInfo SourceInfo { get; init; } = new();
public string? SuccessUrl { get; init; }
}
public record InvoiceItemRequest
{
public string Name { get; init; } = "";
public decimal UnitCost { get; init; }
public int Quantity { get; init; }
public decimal? TaxRate { get; init; }
public string? InvoiceItemTypeId { get; init; }
}
public record CounterPartyInfo
{
public string FirstName { get; init; } = "";
public string LastName { get; init; } = "";
public string Email { get; init; } = "";
}Services
Create Services.cs
// Services.cs
namespace YardPayPro;
public class PaymentsService
{
private readonly YardPayProClient _client;
public PaymentsService(YardPayProClient client) => _client = client;
public Task<PaymentResponse?> PayInvoiceAsync(PayInvoiceRequest request)
=> _client.PostAsync<PaymentResponse>("/api/payment/payinvoice", request);
public Task<List<PaymentMethodInfo>?> GetPaymentMethodsAsync(int invoiceId)
=> _client.GetAsync<List<PaymentMethodInfo>>($"/v1/invoice/{invoiceId}/PaymentMethods");
public Task<List<FundingSource>?> GetFundingSourcesAsync()
=> _client.GetAsync<List<FundingSource>>("/v1/FundingSource");
}
public record FundingSource
{
public int Id { get; init; }
public string Name { get; init; } = "";
public int PaymentOptionId { get; init; }
public string StatusId { get; init; } = "";
public bool IsVerified { get; init; }
public string MaskedAccountNumber { get; init; } = "";
}
public class InvoicesService
{
private readonly YardPayProClient _client;
public InvoicesService(YardPayProClient client) => _client = client;
public Task<Invoice?> GetByReferenceAsync(string code)
=> _client.GetAsync<Invoice>($"/v1/invoice/getByReference(code='{code}')");
public Task<Invoice?> GetByIdAsync(int id)
=> _client.GetAsync<Invoice>($"/v1/invoice/{id}");
public Task<Invoice?> CreateAsync(CreateInvoiceRequest request)
=> _client.PostAsync<Invoice>("/v1/invoice", request);
public Task<List<Invoice>?> ListAsync(string? filter = null, string? orderBy = null, int top = 20, int skip = 0)
{
var qp = new Dictionary<string, string>();
if (filter != null) qp["$filter"] = filter;
if (orderBy != null) qp["$orderby"] = orderBy;
qp["$top"] = top.ToString();
qp["$skip"] = skip.ToString();
return _client.GetAsync<List<Invoice>>("/v1/invoice", qp);
}
public string GetQrCodeUrl(string identifier, string baseUrl = "")
=> $"{baseUrl}/v1/invoice/qrcode/{identifier}";
}ASP.NET Core Usage
// Program.cs — register as singleton
using YardPayPro;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(
new YardPayProClient(builder.Configuration["YardPayPro:ApiKey"]!)
);
builder.Services.AddSingleton<PaymentsService>();
builder.Services.AddSingleton<InvoicesService>();
var app = builder.Build();
// PaymentController.cs
using Microsoft.AspNetCore.Mvc;
using YardPayPro;
[ApiController]
[Route("api/[controller]")]
public class PaymentController : ControllerBase
{
private readonly PaymentsService _payments;
private readonly InvoicesService _invoices;
public PaymentController(PaymentsService payments, InvoicesService invoices)
{
_payments = payments;
_invoices = invoices;
}
[HttpPost("pay")]
public async Task<IActionResult> Pay([FromBody] PayInvoiceRequest request)
{
try
{
var result = await _payments.PayInvoiceAsync(request);
if (result?.IsSuccess == true)
return Ok(result);
return BadRequest(result);
}
catch (YppException ex)
{
return StatusCode(ex.Status, new { ex.Code, ex.Message });
}
}
[HttpGet("invoice/{code}")]
public async Task<IActionResult> GetInvoice(string code)
{
var invoice = await _invoices.GetByReferenceAsync(code);
return invoice != null ? Ok(invoice) : NotFound();
}
}Webhook Signature Verification
// WebhookHelper.cs
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace YardPayPro;
public static class WebhookHelper
{
public static bool VerifySignature(string payload, string signature, string secret)
{
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
var expected = Convert.ToHexString(hash).ToLowerInvariant();
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expected),
Encoding.UTF8.GetBytes(signature)
);
}
public static T? ParseEvent<T>(string payload)
{
return JsonSerializer.Deserialize<T>(payload, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
});
}
}
// Usage in a controller:
[HttpPost("webhooks")]
public async Task<IActionResult> HandleWebhook()
{
var payload = await new StreamReader(Request.Body).ReadToEndAsync();
var signature = Request.Headers["x-ypp-signature"].ToString();
if (!WebhookHelper.VerifySignature(payload, signature, _webhookSecret))
return Unauthorized();
var evt = WebhookHelper.ParseEvent<WebhookEvent>(payload);
// Handle event based on evt.Type
return Ok();
}
public record WebhookEvent
{
public string Type { get; init; } = "";
public JsonElement Data { get; init; }
}Requirements
- .NET 6.0 or later
- No NuGet packages required — uses
System.Net.HttpandSystem.Text.Json - Compatible with ASP.NET Core, Azure Functions, console apps, and worker services