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` builder on `CreateInvoiceParams` (`.iframe(true)`) for embedded
checkout. Like `payment_mode`, it is sent in the request body but excluded from the
HMAC signature.

## [0.1.0]

### Added
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Ships a **Spring Boot starter** for one-line dependency injection.
<dependency>
<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-core</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</dependency>
```

Expand All @@ -43,16 +43,16 @@ Spring Boot apps — use the starter instead (it pulls in the core):
<dependency>
<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-spring-boot-starter</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</dependency>
```

**Gradle**:

```groovy
implementation 'com.getpayin.paylink:paylink-core:0.1.0'
implementation 'com.getpayin.paylink:paylink-core:0.2.0'
// or, for Spring Boot:
implementation 'com.getpayin.paylink:paylink-spring-boot-starter:0.1.0'
implementation 'com.getpayin.paylink:paylink-spring-boot-starter:0.2.0'
```

## Quick start
Expand All @@ -78,6 +78,17 @@ CreateInvoiceResult checkout = paylink.invoices().create(
response.sendRedirect(checkout.checkoutUrl());
```

For an **embedded checkout**, pass `.iframe(true)` and render `checkout.checkoutUrl()`
inside an `<iframe>` instead of redirecting:

```java
CreateInvoiceResult checkout = paylink.invoices().create(
new CreateInvoiceParams()
.firstName("John").lastName("Doe").email("john@example.com")
.orderTitle("Gold Plan").orderAmount("250.00").currency("USD")
.iframe(true));
```

Both credentials are issued in the PayLink dashboard under **Settings → Payment
Integrations**. `publicToken` is sent on every request; `hashToken` is the secret used
only to sign — it never leaves your server.
Expand Down
2 changes: 1 addition & 1 deletion paylink-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-parent</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</parent>

<artifactId>paylink-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* The signed-field registry. Each entry lists a request's fields in the EXACT order
* the server concatenates them when it rebuilds the HMAC signature (the FormRequest
* {@code rules()} order, minus {@code token}/{@code signature}, and minus
* {@code payment_mode} which is sent but not signed). Keep it in lockstep with the
* unsigned fields such as {@code payment_mode} and {@code iframe} which are sent but not
* signed). Keep it in lockstep with the
* server FormRequests under {@code app/Http/Requests/Application/ExternalPaymentIntegration}.
*/
public final class FieldOrders {
Expand All @@ -30,7 +31,8 @@ private FieldOrders() {
FieldSpec.of("redirection_url"),
FieldSpec.of("webhook_url"),
FieldSpec.of("order_details"),
FieldSpec.unsigned("payment_mode")),
FieldSpec.unsigned("payment_mode"),
FieldSpec.unsigned("iframe")),
false);

public static final EndpointSpec PAYMENT_VOID = new EndpointSpec(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ public CreateInvoiceParams paymentMode(String value) {
return this;
}

public CreateInvoiceParams iframe(Boolean value) {
fields.put("iframe", value);
return this;
}

/** Internal: the accumulated snake_case fields. */
public Map<String, Object> fields() {
return fields;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.getpayin.paylink;

import static org.junit.jupiter.api.Assertions.assertEquals;

import com.getpayin.paylink.internal.FieldOrders;
import com.getpayin.paylink.internal.SignedBody;
import com.getpayin.paylink.model.CreateInvoiceParams;
import java.util.Map;
import org.junit.jupiter.api.Test;

/**
* The optional {@code iframe} field is sent in the body (coerced to {@code "1"}/{@code "0"})
* but excluded from the HMAC signature, exactly like {@code payment_mode}.
*/
class IframeOptionTest {

private static final String PUBLIC_TOKEN = "pub_token";
private static final String HASH_TOKEN = "test_hash_token_abc123";

private static CreateInvoiceParams baseParams() {
return new CreateInvoiceParams()
.firstName("John").lastName("Doe").email("john@example.com")
.orderTitle("Gold Plan").orderAmount("250.00").currency("USD");
}

@Test
void iframeTrueSerializesToOneInBody() {
Map<String, String> body = SignedBody.build(
FieldOrders.INVOICE_CREATE, baseParams().iframe(true).fields(), PUBLIC_TOKEN, HASH_TOKEN);

assertEquals("1", body.get("iframe"));
}

@Test
void iframeFalseSerializesToZeroInBody() {
Map<String, String> body = SignedBody.build(
FieldOrders.INVOICE_CREATE, baseParams().iframe(false).fields(), PUBLIC_TOKEN, HASH_TOKEN);

assertEquals("0", body.get("iframe"));
}

@Test
void iframeIsUnsignedSoItDoesNotChangeTheSignature() {
Map<String, String> without = SignedBody.build(
FieldOrders.INVOICE_CREATE, baseParams().fields(), PUBLIC_TOKEN, HASH_TOKEN);
Map<String, String> with = SignedBody.build(
FieldOrders.INVOICE_CREATE, baseParams().iframe(true).fields(), PUBLIC_TOKEN, HASH_TOKEN);

// iframe is present in the body but must not affect the HMAC signature.
assertEquals("1", with.get("iframe"));
assertEquals(without.get("signature"), with.get("signature"));
}
}
4 changes: 2 additions & 2 deletions paylink-spring-boot-starter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-parent</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</parent>

<artifactId>paylink-spring-boot-starter</artifactId>
Expand All @@ -18,7 +18,7 @@
<dependency>
<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-core</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.getpayin.paylink</groupId>
<artifactId>paylink-parent</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
<packaging>pom</packaging>

<name>PayLink Java SDK (parent)</name>
Expand Down
Loading