diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b4651e..73f6f15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/README.md b/README.md
index 5d0ba29..06f25b5 100644
--- a/README.md
+++ b/README.md
@@ -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);
diff --git a/src/Paylink/Internal/FieldOrders.cs b/src/Paylink/Internal/FieldOrders.cs
index d619850..81d2b18 100644
--- a/src/Paylink/Internal/FieldOrders.cs
+++ b/src/Paylink/Internal/FieldOrders.cs
@@ -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(
diff --git a/src/Paylink/Paylink.csproj b/src/Paylink/Paylink.csproj
index 4103ed7..f034b21 100644
--- a/src/Paylink/Paylink.csproj
+++ b/src/Paylink/Paylink.csproj
@@ -9,7 +9,7 @@
$(NoWarn);CS1591
Paylink
- 0.1.0
+ 0.2.0
GetPayIn
Official server-side .NET SDK for the PayLink / GetPayIn payment integration API (checkouts, payment operations, card tokens, recurring mandates, webhook verification).
paylink;getpayin;payments;payment-gateway;checkout;webhooks;cybersource
diff --git a/src/Paylink/Resources/Invoices.cs b/src/Paylink/Resources/Invoices.cs
index 95086d7..19c8a64 100644
--- a/src/Paylink/Resources/Invoices.cs
+++ b/src/Paylink/Resources/Invoices.cs
@@ -42,6 +42,7 @@ public async Task CreateAsync(
{ "webhook_url", parameters.WebhookUrl },
{ "order_details", parameters.OrderDetails },
{ "payment_mode", parameters.PaymentMode },
+ { "iframe", parameters.Iframe },
};
var data = await Api
diff --git a/src/Paylink/Types/Params.cs b/src/Paylink/Types/Params.cs
index d875cd7..d22690e 100644
--- a/src/Paylink/Types/Params.cs
+++ b/src/Paylink/Types/Params.cs
@@ -36,6 +36,8 @@ public sealed class CreateInvoiceParams
public string? OrderDetails { get; set; }
public string? PaymentMode { get; set; }
+
+ public bool? Iframe { get; set; }
}
///
diff --git a/tests/Paylink.Tests/IframeUnsignedTests.cs b/tests/Paylink.Tests/IframeUnsignedTests.cs
new file mode 100644
index 0000000..20cb8f8
--- /dev/null
+++ b/tests/Paylink.Tests/IframeUnsignedTests.cs
@@ -0,0 +1,78 @@
+using System.Collections.Generic;
+using Paylink.Internal;
+using Xunit;
+
+namespace Paylink.Tests
+{
+ ///
+ /// The optional iframe field on invoice create is sent in the body but excluded
+ /// from the HMAC signature — exactly like payment_mode. These tests build the
+ /// signed body directly (as the golden tests do) to prove both properties.
+ ///
+ public sealed class IframeUnsignedTests
+ {
+ private const string PublicToken = "pub_token";
+ private const string HashToken = "hash_token";
+
+ private static Dictionary BaseInput()
+ {
+ return new Dictionary
+ {
+ { "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(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"]);
+ }
+ }
+}