Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.0]

### Added

- Optional `Iframe` parameter on `CreateInvoiceParams` to enable embedded (iframe)
checkout. Like `PaymentMode`, it is sent in the request body but excluded from the
HMAC signature.

## [0.1.0]

### Added
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public sealed class CheckoutController : ControllerBase
Currency = "USD",
RedirectionUrl = "https://shop.example.com/return",
WebhookUrl = "https://shop.example.com/webhooks/paylink",
// Iframe = true, // optional: enable embedded (iframe) checkout
});

return Redirect(checkout.CheckoutUrl);
Expand Down
1 change: 1 addition & 0 deletions src/Paylink/Internal/FieldOrders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ internal static class FieldOrders
new FieldSpec("webhook_url"),
new FieldSpec("order_details"),
new FieldSpec("payment_mode", false),
new FieldSpec("iframe", false),
});

public static readonly EndpointSpec PaymentVoid = new EndpointSpec(
Expand Down
2 changes: 1 addition & 1 deletion src/Paylink/Paylink.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<NoWarn>$(NoWarn);CS1591</NoWarn>

<PackageId>Paylink</PackageId>
<Version>0.1.0</Version>
<Version>0.2.0</Version>
<Authors>GetPayIn</Authors>
<Description>Official server-side .NET SDK for the PayLink / GetPayIn payment integration API (checkouts, payment operations, card tokens, recurring mandates, webhook verification).</Description>
<PackageTags>paylink;getpayin;payments;payment-gateway;checkout;webhooks;cybersource</PackageTags>
Expand Down
1 change: 1 addition & 0 deletions src/Paylink/Resources/Invoices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public async Task<CreateInvoiceResult> CreateAsync(
{ "webhook_url", parameters.WebhookUrl },
{ "order_details", parameters.OrderDetails },
{ "payment_mode", parameters.PaymentMode },
{ "iframe", parameters.Iframe },
};

var data = await Api
Expand Down
2 changes: 2 additions & 0 deletions src/Paylink/Types/Params.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public sealed class CreateInvoiceParams
public string? OrderDetails { get; set; }

public string? PaymentMode { get; set; }

public bool? Iframe { get; set; }
}

/// <summary>
Expand Down
78 changes: 78 additions & 0 deletions tests/Paylink.Tests/IframeUnsignedTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Collections.Generic;
using Paylink.Internal;
using Xunit;

namespace Paylink.Tests
{
/// <summary>
/// The optional <c>iframe</c> field on invoice create is sent in the body but excluded
/// from the HMAC signature — exactly like <c>payment_mode</c>. These tests build the
/// signed body directly (as the golden tests do) to prove both properties.
/// </summary>
public sealed class IframeUnsignedTests
{
private const string PublicToken = "pub_token";
private const string HashToken = "hash_token";

private static Dictionary<string, object?> BaseInput()
{
return new Dictionary<string, object?>
{
{ "first_name", "John" },
{ "last_name", "Doe" },
{ "email", "john@example.com" },
{ "order_title", "Gold Plan" },
{ "order_amount", "250.00" },
{ "currency", "USD" },
{ "redirection_url", "https://shop.example.com/return" },
{ "webhook_url", "https://shop.example.com/webhooks/paylink" },
};
}

[Fact]
public void IframeTrueGoesIntoBodyAsOne()
{
var input = BaseInput();
input["iframe"] = true;

var body = SignedBody.Build(FieldOrders.InvoiceCreate, input, PublicToken, HashToken);

Assert.Equal("1", body["iframe"]);
}

[Fact]
public void IframeFalseGoesIntoBodyAsZero()
{
var input = BaseInput();
input["iframe"] = false;

var body = SignedBody.Build(FieldOrders.InvoiceCreate, input, PublicToken, HashToken);

Assert.Equal("0", body["iframe"]);
}

[Fact]
public void OmittedIframeIsAbsentFromBody()
{
var body = SignedBody.Build(FieldOrders.InvoiceCreate, BaseInput(), PublicToken, HashToken);

Assert.False(body.ContainsKey("iframe"));
}

[Fact]
public void IframeDoesNotChangeTheSignature()
{
var withoutIframe = SignedBody.Build(FieldOrders.InvoiceCreate, BaseInput(), PublicToken, HashToken);

var withIframe = new Dictionary<string, object?>(BaseInput())
{
{ "iframe", true },
};
var signed = SignedBody.Build(FieldOrders.InvoiceCreate, withIframe, PublicToken, HashToken);

// iframe is present in the body but must not alter the signature.
Assert.Equal("1", signed["iframe"]);
Assert.Equal(withoutIframe["signature"], signed["signature"]);
}
}
}
Loading