Skip to content
Open
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
69 changes: 69 additions & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- [Connection](#connection)
- [Usage](#usage)
- [QR code registration](#qr-code-registration)
- [QR code registration with subscription](#subscription-for-payment)
- [Get info for registered QR code](#get-info-for-qr-code-registered)
- [Get payment info](#get-payment-info)
- [Create refund for payment](#create-refund-for-payment)
Expand Down Expand Up @@ -111,6 +112,7 @@ public class AppExample {
qrCode.setAdditionalInfo("Additional info");
qrCode.setPaymentDetails("Payment details");
qrCode.setQrExpirationDate(ZonedDateTime.now().plusDays(1));
qrCode.setQrDescription("QR code description");
QRUrl response = client.registerQR(qrCode);
response.getQrId();
response.getOrUrl();
Expand Down Expand Up @@ -152,6 +154,7 @@ qrCode.setAccount("40700000000000000000");
qrCode.setAdditionalInfo("Additional info");
qrCode.setPaymentDetails("Payment details");
qrCode.setQrExpirationDate("2023-07-22T09:14:38.107227+03:00");
qrCode.setQRDescription("QR code description");

QRUrl response = client.registerQR(qrCode);

Expand Down Expand Up @@ -204,6 +207,72 @@ QRDynamic qrDynamic = new QRDynamic(order, new BigDecimal(100));
qrDynamic.setQrExpirationDate("+1d5m"); // + 1 day 5 minutes
~~~

There is also an option to pass a field with a link for automatically returning the payer from the bank's application to the store's application or website.
For this, the redirectUrl field should contain https:// for web pages or a unique scheme for the mobile application.

Example:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRStatic qrStatic = new QRStatic(order);
qrStatic.redirectUrl("https://site_shop/about_payment");
~~~

There is also an option to pass a set of fields as key, value pairs by placing the values in the extra field.

Example:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRStatic qrStatic = new QRStatic(order);

Extra extra = new Extra();
extra.put("extraParam1", "Example of extra parameter 1");
extra.put("extraParam2", "Example of extra parameter 2");
qrStatic.setExtra(extra);
~~~

### Subscription for Payment

There is also an option for the dynamic QR code type `QRDynamic` to pass a parameter for enabling a payment subscription by filling in the `subscription` field.
The `subscriptionPurpose` field is mandatory and should contain a description of the subscription that the client will see in the bank's application.
The `subscription` object includes an `extra` field that contains additional fields for free-form `key-value` pairs,
as well as an `autoCharge` field that holds the `AutoCharge` class, whose fields are mandatory.
If the `autoCharge` object is passed, the `extra` field of the `Subscription` class must include a key that will be returned in the callback notification body.
This will allow you to correlate the subscription and its automatic payments.

Example:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRDynamic qrDynamic = new QRDynamic(order, new BigDecimal(100));
Extra extra = new Extra();
extra.put("extraParam1", "Example of extra parameter 1");
extra.put("extraParam2", "Example of extra parameter 2");
qrDynamic.setExtra(extra);
qrDynamic.setRedirectUrl("https://cool-company.zone/paid");
Subscription subscription = new Subscription("Service subscription");
subscription.setId(TestUtils.getRandomUUID());
AutoCharge autoCharge = new AutoCharge();
autoCharge.setFrequency("MONTHLY");
autoCharge.setAmount(new BigDecimal(100));
autoCharge.setFirstChargeDate(LocalDate.now().plusDays(7));
subscription.setAutoCharge(autoCharge);
subscription.setExtra((Extra) new Extra().put("key1", "value1"));
qrDynamic.setSubscription(subscription);
~~~



## Get info for QR code registered

To get info about QR code you should create `QRId` class instance passing QR identifier to its constructor, then invoke `getQRInfo(QRId)` method:
Expand Down
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- [Подключение](#подключение)
- [Использование](#использование)
- [Регистрация QR-кода](#регистрация-qr-кода)
- [Регистрация QR-кода с подпиской](#подписка-на-оплату)
- [Получение данных по зарегистрированному ранее QR-коду](#получение-данных-по-зарегистрированному-ранее-qr-коду)
- [Получение информации по платежу](#получение-информации-по-платежу)
- [Оформление возврата по платежу](#оформление-возврата-по-платежу)
Expand Down Expand Up @@ -115,6 +116,7 @@ public class AppExample {
qrCode.setAdditionalInfo("Доп информация");
qrCode.setPaymentDetails("Назначение платежа");
qrCode.setQrExpirationDate(ZonedDateTime.now().plusDays(1));
qrCode.setQrDescription("Описание QR кода");
QRUrl response = client.registerQR(qrCode);
response.getQrId();
response.getOrUrl();
Expand Down Expand Up @@ -142,6 +144,7 @@ public class AppExample {
Обязательные параметры:
- номер заказа в системе партнера `order(String)`
- (*`QRDynamic`*) сумма в рублях `amount(BigDecimal)`
- идентификатор зарегистрированного партнера в СБП `sbpMerchantId(String)`

Опциональные параметры могут быть заполнены с помощью set методов.

Expand All @@ -157,6 +160,7 @@ qrCode.setAccount("40700000000000000000");
qrCode.setAdditionalInfo("Доп информация");
qrCode.setPaymentDetails("Назначение платежа");
qrCode.setQrExpirationDate("2023-07-22T09:14:38.107227+03:00");
qrCode.setQrDescription("Описание QR кода");

QRUrl response = client.registerQR(qrCode);

Expand Down Expand Up @@ -209,6 +213,71 @@ QRDynamic qrDynamic = new QRDynamic(order, new BigDecimal(100));
qrDynamic.setQrExpirationDate("+1d5m"); // + 1 day 5 minutes
~~~

Также существует возможность передать поле со ссылкой для автоматического возврата плательщика из приложения банка в приложение или на сайт магазина.
Для этого в поле `redirectUrl` Ссылка должна содержать `https://` для web страниц или уникальную схему для мобильного приложения.

Пример:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRStatic qrStatic = new QRStatic(order);
qrStatic.redirectUrl("https://сайт_магазина/страница_об_оплате");
~~~

Также существует возможность передать набор полей в виде значений `ключ, значение` поместив значения в поле `extra`

Пример:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRStatic qrStatic = new QRStatic(order);

Extra extra = new Extra();
extra.put("extraParam1", "Пример экстра параметра 1");
extra.put("extraParam2", "Пример экстра параметра 2");
qrStatic.setExtra(extra);
~~~

### Подписка на оплату

Также существует возможность для динамического типа QR кода `QRDynamic` передать параметр для включения подписки на оплату, заполнив поле `subscription`.
Обязательное поле к заполнению `subscriptionPurpose` описание подписки, которое клиент увидит в приложении банка.
В объекте `subscription` есть поле `extra` которое содержит дополнительные поля для свободного заполнения по принципу key-value,
а также есть поле `autoCharge` в котором хранится класс `AutoCharge` поля которого обязательны для заполнения.
Если передан объект `autoCharge`, то в поле `extra` класса `Subscription` необходимо передать ключ, который вернется в теле callback-уведомления.
Это позволит соотнести подписку и автоматические платежи по ней

Пример:

~~~ java
String order = QRUtil.generateOrderNumber(); // UUID_v4

// save order in a database;

QRDynamic qrDynamic = new QRDynamic(order, new BigDecimal(100));
Extra extra = new Extra();
extra.put("extraParam1", "Пример экстра параметра 1");
extra.put("extraParam2", "Пример экстра параметра 2");
qrDynamic.setExtra(extra);
qrDynamic.setRedirectUrl("https://cool-company.zone/paid");
Subscription subscription = new Subscription("Подписка на услуги");
subscription.setId(TestUtils.getRandomUUID());
AutoCharge autoCharge = new AutoCharge();
autoCharge.setFrequency("MONTHLY");
autoCharge.setAmount(new BigDecimal(100));
autoCharge.setFirstChargeDate(LocalDate.now().plusDays(7));
subscription.setAutoCharge(autoCharge);
subscription.setExtra((Extra) new Extra().put("key1", "value1"));
qrDynamic.setSubscription(subscription);
~~~


## Получение данных по зарегистрированному ранее QR-коду

Необходимо создать объект класса `QRId`, передав в конструкторе идентификатор QR-кода, и вызвать метод `getQRInfo(QRId)`:
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>raiffeisen</groupId>
<artifactId>sbp-sdk-java</artifactId>
<version>1.0.8</version>
<version>1.0.9</version>
<packaging>jar</packaging>

<properties>
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/AutoCharge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package raiffeisen.sbp.sdk.model.out;

import lombok.Data;

import java.math.BigDecimal;
import java.time.LocalDate;

@Data
public class AutoCharge {
private String frequency;
private LocalDate firstChargeDate;
private BigDecimal amount;
}
10 changes: 10 additions & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/Extra.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package raiffeisen.sbp.sdk.model.out;

import lombok.Getter;
import lombok.Setter;

import java.util.HashMap;

@Setter @Getter
public class Extra extends HashMap<String, Object> {
}
4 changes: 4 additions & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/QR.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public abstract class QR {
protected String createDate;
protected String paymentDetails;
protected String qrExpirationDate;
protected String qrDescription;
protected String redirectUrl;
protected Extra extra;
protected Subscription subscription;

public abstract QR newInstance();

Expand Down
15 changes: 8 additions & 7 deletions src/main/java/raiffeisen/sbp/sdk/model/out/QRDynamic.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ public QRDynamic(String order, BigDecimal amount) {

@Override
public QR newInstance() {
QRDynamic qrStatic = new QRDynamic(order, amount);
qrStatic.setAccount(account);
qrStatic.setAdditionalInfo(additionalInfo);
qrStatic.setCreateDate(createDate);
qrStatic.setPaymentDetails(paymentDetails);
qrStatic.setQrExpirationDate(qrExpirationDate);
return qrStatic;
QRDynamic qrDynamic = new QRDynamic(order, amount);
qrDynamic.setAccount(account);
qrDynamic.setAdditionalInfo(additionalInfo);
qrDynamic.setCreateDate(createDate);
qrDynamic.setPaymentDetails(paymentDetails);
qrDynamic.setQrExpirationDate(qrExpirationDate);
qrDynamic.setQrDescription(qrDescription);
return qrDynamic;
}
}
1 change: 1 addition & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/QRStatic.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public QRStatic newInstance() {
qrStatic.setCreateDate(createDate);
qrStatic.setPaymentDetails(paymentDetails);
qrStatic.setQrExpirationDate(qrExpirationDate);
qrStatic.setQrDescription(qrDescription);
return qrStatic;
}
}
1 change: 1 addition & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/QRVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public QRVariable() {
public QR newInstance() {
QRVariable qrVariable = new QRVariable();
qrVariable.setAccount(account);
qrVariable.setQrDescription(qrDescription);
return qrVariable;
}
}
13 changes: 13 additions & 0 deletions src/main/java/raiffeisen/sbp/sdk/model/out/Subscription.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package raiffeisen.sbp.sdk.model.out;

import lombok.Data;
import lombok.NonNull;

@Data
public class Subscription {
private String id;
@NonNull
private String subscriptionPurpose;
private AutoCharge autoCharge;
private Extra extra;
}
48 changes: 47 additions & 1 deletion src/test/java/raiffeisen/sbp/sdk/CreateQrTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,24 @@
import raiffeisen.sbp.sdk.exception.ContractViolationException;
import raiffeisen.sbp.sdk.exception.SbpException;
import raiffeisen.sbp.sdk.model.in.QRUrl;
import raiffeisen.sbp.sdk.model.out.AutoCharge;
import raiffeisen.sbp.sdk.model.out.Extra;
import raiffeisen.sbp.sdk.model.out.QRDynamic;
import raiffeisen.sbp.sdk.model.out.QRStatic;
import raiffeisen.sbp.sdk.model.out.QRVariable;
import raiffeisen.sbp.sdk.model.out.Subscription;

import java.io.IOException;
import java.math.BigDecimal;
import java.net.URISyntaxException;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

@Tag("integration")
class CreateQrTest {
Expand Down Expand Up @@ -59,19 +67,57 @@ void createQRMaxTest() throws IOException, ContractViolationException, SbpExcept
qrStatic.setAdditionalInfo("Доп информация");
qrStatic.setAmount(new BigDecimal(1110));
qrStatic.setPaymentDetails("Назначение платежа");
qrStatic.setQrExpirationDate("2023-07-22T09:14:38.107227+03:00");
qrStatic.setQrExpirationDate("2025-07-22T09:14:38.107227+03:00");
qrStatic.setQrDescription("Описание QR кода");

QRUrl response = TestUtils.CLIENT.registerQR(qrStatic);
assertNotNull(response.getQrId());
assertNotNull(response.getQrUrl());
assertNotNull(response.getPayload());
}

@Test
void createQRWithSubscription() throws IOException, ContractViolationException, SbpException, URISyntaxException, InterruptedException {
QRDynamic qrDynamic = new QRDynamic(TestUtils.getRandomUUID(), new BigDecimal(100));
qrDynamic.setAdditionalInfo("Доп информация");
qrDynamic.setPaymentDetails("Назначение платежа");
qrDynamic.setQrExpirationDate(ZonedDateTime.now().plusDays(90));
qrDynamic.setQrDescription("Описание QR кода");
Extra extra = new Extra();
extra.put("extraParam1", "Пример экстра параметра 1");
extra.put("extraParam2", "Пример экстра параметра 2");
qrDynamic.setExtra(extra);
qrDynamic.setRedirectUrl("https://cool-company.zone/paid");
Subscription subscription = new Subscription("Подписка на услуги");
subscription.setId(TestUtils.getRandomUUID());
AutoCharge autoCharge = new AutoCharge();
autoCharge.setFrequency("MONTHLY");
autoCharge.setAmount(new BigDecimal(100));
autoCharge.setFirstChargeDate(LocalDate.now().plusDays(7));
subscription.setAutoCharge(autoCharge);
subscription.setExtra((Extra) new Extra().put("key1", "value1"));
qrDynamic.setSubscription(subscription);

QRUrl response = TestUtils.CLIENT.registerQR(qrDynamic);
assertNotNull(response.getQrId());
assertNotNull(response.getQrUrl());
assertTrue(isValidDateFormat(autoCharge.getFirstChargeDate().toString()), "Дата не соответствует формату YYYY-MM-DD");
}

@Test
void createQRWithoutAmountNegativeTest() {
QRStatic badQR = new QRStatic("");

assertThrows(SbpException.class, () -> TestUtils.CLIENT.registerQR(badQR));
}

private boolean isValidDateFormat(String value) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А эта логика нужна только в рамках теста?

DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
try {
LocalDate.parse(value, dateTimeFormatter);
return true;
} catch ( DateTimeParseException e) {
return false;
}
}
}
2 changes: 1 addition & 1 deletion src/test/java/raiffeisen/sbp/sdk/data/TestData.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public final class TestData {
"\"sbpMerchantId\":\"MA341037\"" +
"}";
public static final String MISSING_REFUND_ID_ERROR_CODE = "ERROR.INVALID_REQUEST";
public static final String MISSING_REFUND_ID_ERROR_MESSAGE = "Не передан обязательный параметр";
public static final String MISSING_REFUND_ID_ERROR_MESSAGE = "Поле refundId Не передан обязательный параметр";
public static final String QR_CODE_NOT_FOUND_ERROR_CODE = "ERROR.NOT_FOUND";
public static final String QR_CODE_NOT_FOUND_ERROR_MESSAGE = "QR-код не найден у данного партнера";
public static final String REFUND_NOT_FOUND_ERROR_MESSAGE = "Возврат с refundId %s не найден";
Expand Down