diff --git a/api-schema/src/main/java/org/stellar/anchor/api/callback/SendEventRequestPayload.java b/api-schema/src/main/java/org/stellar/anchor/api/callback/SendEventRequestPayload.java index 3b900f975e..4785202b20 100644 --- a/api-schema/src/main/java/org/stellar/anchor/api/callback/SendEventRequestPayload.java +++ b/api-schema/src/main/java/org/stellar/anchor/api/callback/SendEventRequestPayload.java @@ -4,6 +4,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.stellar.anchor.api.event.AnchorEvent; +import org.stellar.anchor.api.platform.CustomerUpdatedResponse; import org.stellar.anchor.api.platform.GetQuoteResponse; import org.stellar.anchor.api.platform.GetTransactionResponse; @@ -13,6 +14,7 @@ public class SendEventRequestPayload { GetTransactionResponse transaction; GetQuoteResponse quote; + CustomerUpdatedResponse customer; /** * Creates a SendEventRequestPayload from an AnchorEvent. @@ -23,6 +25,8 @@ public class SendEventRequestPayload { public static SendEventRequestPayload from(AnchorEvent event) { SendEventRequestPayload payload = new SendEventRequestPayload(); switch (event.getType()) { + case CUSTOMER_UPDATED: + payload.setCustomer(event.getCustomer()); case QUOTE_CREATED: payload.setQuote(event.getQuote()); case TRANSACTION_CREATED: diff --git a/api-schema/src/main/java/org/stellar/anchor/api/exception/SepCustomerInfoNeededException.java b/api-schema/src/main/java/org/stellar/anchor/api/exception/SepCustomerInfoNeededException.java new file mode 100644 index 0000000000..49eb2d0aa4 --- /dev/null +++ b/api-schema/src/main/java/org/stellar/anchor/api/exception/SepCustomerInfoNeededException.java @@ -0,0 +1,12 @@ +package org.stellar.anchor.api.exception; + +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** Thrown when a customer's info is needed to complete a request. */ +@RequiredArgsConstructor +@Getter +public class SepCustomerInfoNeededException extends AnchorException { + private final List fields; +} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/sep/CustomerInfoNeededResponse.java b/api-schema/src/main/java/org/stellar/anchor/api/sep/CustomerInfoNeededResponse.java new file mode 100644 index 0000000000..25ac39a272 --- /dev/null +++ b/api-schema/src/main/java/org/stellar/anchor/api/sep/CustomerInfoNeededResponse.java @@ -0,0 +1,12 @@ +package org.stellar.anchor.api.sep; + +import java.util.List; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Data +public class CustomerInfoNeededResponse { + private final String type = "non_interactive_customer_info_needed"; + private final List fields; +} diff --git a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep12/Sep12Status.java b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep12/Sep12Status.java index fb7c9b992a..7d5cfd23eb 100644 --- a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep12/Sep12Status.java +++ b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep12/Sep12Status.java @@ -13,10 +13,7 @@ public enum Sep12Status { PROCESSING("PROCESSING"), @SerializedName("REJECTED") - REJECTED("REJECTED"), - - @SerializedName("VERIFICATION_REQUIRED") - VERIFICATION_REQUIRED("VERIFICATION_REQUIRED"); + REJECTED("REJECTED"); private final String name; diff --git a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawExchangeRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawExchangeRequest.java index 048413e341..3888ead1b9 100644 --- a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawExchangeRequest.java +++ b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawExchangeRequest.java @@ -33,6 +33,9 @@ public class StartWithdrawExchangeRequest { @SerializedName("quote_id") String quoteId; + /** The account to withdraw from. */ + String account; + /** The amount of the source asset the user would like to withdraw. */ @NonNull String amount; diff --git a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawRequest.java b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawRequest.java index c60fbc2d2b..fe30c031da 100644 --- a/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawRequest.java +++ b/api-schema/src/main/java/org/stellar/anchor/api/sep/sep6/StartWithdrawRequest.java @@ -23,6 +23,9 @@ public class StartWithdrawRequest { /** Type of withdrawal. */ String type; + /** The account to withdraw from. */ + String account; + /** The amount to withdraw. */ String amount; diff --git a/core/src/main/java/org/stellar/anchor/sep6/RequestValidator.java b/core/src/main/java/org/stellar/anchor/sep6/RequestValidator.java index 4193a4500c..677cc652ff 100644 --- a/core/src/main/java/org/stellar/anchor/sep6/RequestValidator.java +++ b/core/src/main/java/org/stellar/anchor/sep6/RequestValidator.java @@ -1,10 +1,14 @@ package org.stellar.anchor.sep6; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import org.stellar.anchor.api.exception.SepValidationException; +import org.stellar.anchor.api.callback.CustomerIntegration; +import org.stellar.anchor.api.callback.GetCustomerRequest; +import org.stellar.anchor.api.callback.GetCustomerResponse; +import org.stellar.anchor.api.exception.*; import org.stellar.anchor.api.sep.AssetInfo; import org.stellar.anchor.asset.AssetService; import org.stellar.sdk.KeyPair; @@ -13,6 +17,7 @@ @RequiredArgsConstructor public class RequestValidator { @NonNull private final AssetService assetService; + @NonNull private final CustomerIntegration customerIntegration; /** * Validates that the requested asset is valid and enabled for deposit. @@ -102,11 +107,33 @@ public void validateTypes(String requestType, String assetCode, List val * @param account the account * @throws SepValidationException if the account is invalid */ - public void validateAccount(String account) throws SepValidationException { + public void validateAccount(String account) throws AnchorException { try { KeyPair.fromAccountId(account); } catch (RuntimeException ex) { throw new SepValidationException(String.format("invalid account %s", account)); } + + GetCustomerRequest request = GetCustomerRequest.builder().account(account).build(); + GetCustomerResponse response = customerIntegration.getCustomer(request); + + if (response == null || response.getStatus() == null) { + throw new ServerErrorException("unable to get required fields for customer") {}; + } + + switch (response.getStatus()) { + case "NEEDS_INFO": + throw new SepCustomerInfoNeededException(new ArrayList<>(response.getFields().keySet())); + case "PROCESSING": + throw new SepNotAuthorizedException("customer is being reviewed by anchor"); + case "REJECTED": + throw new SepNotAuthorizedException("customer rejected by anchor"); + case "ACCEPTED": + // do nothing + break; + default: + throw new ServerErrorException( + String.format("unknown customer status: %s", response.getStatus())); + } } } diff --git a/core/src/main/java/org/stellar/anchor/sep6/Sep6Service.java b/core/src/main/java/org/stellar/anchor/sep6/Sep6Service.java index 91538673ca..72203f1b2d 100644 --- a/core/src/main/java/org/stellar/anchor/sep6/Sep6Service.java +++ b/core/src/main/java/org/stellar/anchor/sep6/Sep6Service.java @@ -227,6 +227,8 @@ public StartWithdrawResponse withdraw(Sep10Jwt token, StartWithdrawRequest reque asset.getWithdraw().getMinAmount(), asset.getWithdraw().getMaxAmount()); } + String sourceAccount = request.getAccount() != null ? request.getAccount() : token.getAccount(); + requestValidator.validateAccount(sourceAccount); String id = SepHelper.generateSepTransactionId(); @@ -245,7 +247,7 @@ public StartWithdrawResponse withdraw(Sep10Jwt token, StartWithdrawRequest reque .sep10AccountMemo(token.getAccountMemo()) .memo(generateMemo(id)) .memoType(memoTypeAsString(MEMO_HASH)) - .fromAccount(token.getAccount()) + .fromAccount(sourceAccount) .withdrawAnchorAccount(asset.getDistributionAccount()) .toAccount(asset.getDistributionAccount()) .refundMemo(request.getRefundMemo()) @@ -295,6 +297,8 @@ public StartWithdrawResponse withdrawExchange( sellAsset.getSignificantDecimals(), sellAsset.getWithdraw().getMinAmount(), sellAsset.getWithdraw().getMaxAmount()); + String sourceAccount = request.getAccount() != null ? request.getAccount() : token.getAccount(); + requestValidator.validateAccount(sourceAccount); String id = SepHelper.generateSepTransactionId(); @@ -330,7 +334,7 @@ public StartWithdrawResponse withdrawExchange( .sep10AccountMemo(token.getAccountMemo()) .memo(generateMemo(id)) .memoType(memoTypeAsString(MEMO_HASH)) - .fromAccount(token.getAccount()) + .fromAccount(sourceAccount) .withdrawAnchorAccount(sellAsset.getDistributionAccount()) .refundMemo(request.getRefundMemo()) .refundMemoType(request.getRefundMemoType()) diff --git a/core/src/test/kotlin/org/stellar/anchor/sep6/RequestValidatorTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep6/RequestValidatorTest.kt index 044b6aee9b..f3e75f44c3 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep6/RequestValidatorTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep6/RequestValidatorTest.kt @@ -1,9 +1,8 @@ package org.stellar.anchor.sep6 -import io.mockk.MockKAnnotations -import io.mockk.every +import io.mockk.* import io.mockk.impl.annotations.MockK -import io.mockk.mockk +import kotlin.test.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -11,19 +10,28 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import org.stellar.anchor.TestConstants.Companion.TEST_ACCOUNT import org.stellar.anchor.TestConstants.Companion.TEST_ASSET +import org.stellar.anchor.api.callback.CustomerIntegration +import org.stellar.anchor.api.callback.GetCustomerRequest +import org.stellar.anchor.api.callback.GetCustomerResponse +import org.stellar.anchor.api.exception.SepCustomerInfoNeededException +import org.stellar.anchor.api.exception.SepNotAuthorizedException import org.stellar.anchor.api.exception.SepValidationException +import org.stellar.anchor.api.exception.ServerErrorException import org.stellar.anchor.api.sep.AssetInfo +import org.stellar.anchor.api.sep.sep12.Sep12Status +import org.stellar.anchor.api.shared.CustomerField import org.stellar.anchor.asset.AssetService class RequestValidatorTest { @MockK(relaxed = true) lateinit var assetService: AssetService + @MockK(relaxed = true) lateinit var customerIntegration: CustomerIntegration private lateinit var requestValidator: RequestValidator @BeforeEach fun setup() { MockKAnnotations.init(this, relaxUnitFun = true) - requestValidator = RequestValidator(assetService) + requestValidator = RequestValidator(assetService, customerIntegration) } @Test @@ -140,11 +148,63 @@ class RequestValidatorTest { @Test fun `test validateAccount`() { + every { + customerIntegration.getCustomer(GetCustomerRequest.builder().account(TEST_ACCOUNT).build()) + } returns GetCustomerResponse.builder().status(Sep12Status.ACCEPTED.name).build() requestValidator.validateAccount(TEST_ACCOUNT) } @Test fun `test validateAccount with invalid account`() { assertThrows { requestValidator.validateAccount("??") } + + verify { customerIntegration wasNot called } + } + + @Test + fun `test validateAccount customerIntegration failure`() { + every { + customerIntegration.getCustomer(GetCustomerRequest.builder().account(TEST_ACCOUNT).build()) + } throws RuntimeException("test") + assertThrows { requestValidator.validateAccount(TEST_ACCOUNT) } + } + + @Test + fun `test validateAccount with needs info customer`() { + every { + customerIntegration.getCustomer(GetCustomerRequest.builder().account(TEST_ACCOUNT).build()) + } returns + GetCustomerResponse.builder() + .status(Sep12Status.NEEDS_INFO.name) + .fields(mapOf("first_name" to CustomerField.builder().build())) + .build() + val ex = + assertThrows { + requestValidator.validateAccount(TEST_ACCOUNT) + } + assertEquals(listOf("first_name"), ex.fields) + } + + @Test + fun `test validateAccount with processing customer`() { + every { + customerIntegration.getCustomer(GetCustomerRequest.builder().account(TEST_ACCOUNT).build()) + } returns GetCustomerResponse.builder().status(Sep12Status.PROCESSING.name).build() + assertThrows { requestValidator.validateAccount(TEST_ACCOUNT) } + } + + @Test + fun `test validateAccount with rejected customer`() { + every { + customerIntegration.getCustomer(GetCustomerRequest.builder().account(TEST_ACCOUNT).build()) + } returns GetCustomerResponse.builder().status(Sep12Status.REJECTED.name).build() + assertThrows { requestValidator.validateAccount(TEST_ACCOUNT) } + } + + @Test + fun `test validateAccount with unknown status customer`() { + every { customerIntegration.getCustomer(any()) } returns + GetCustomerResponse.builder().status("??").build() + assertThrows { requestValidator.validateAccount(TEST_ACCOUNT) } } } diff --git a/core/src/test/kotlin/org/stellar/anchor/sep6/Sep6ServiceTest.kt b/core/src/test/kotlin/org/stellar/anchor/sep6/Sep6ServiceTest.kt index 9c7c4e05f6..bbd07b80e7 100644 --- a/core/src/test/kotlin/org/stellar/anchor/sep6/Sep6ServiceTest.kt +++ b/core/src/test/kotlin/org/stellar/anchor/sep6/Sep6ServiceTest.kt @@ -4,7 +4,7 @@ import com.google.gson.Gson import io.mockk.* import io.mockk.impl.annotations.MockK import java.time.Instant -import java.util.UUID +import java.util.* import kotlin.test.assertEquals import kotlin.test.assertNotNull import org.junit.jupiter.api.BeforeEach @@ -363,6 +363,7 @@ class Sep6ServiceTest { asset.deposit.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { @@ -443,6 +444,7 @@ class Sep6ServiceTest { asset.deposit.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { exchangeAmountsCalculator.calculate(any(), any(), "100", TEST_ACCOUNT) } @@ -629,6 +631,7 @@ class Sep6ServiceTest { asset.deposit.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { txnStore.save(any()) } @@ -668,6 +671,7 @@ class Sep6ServiceTest { asset.withdraw.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { txnStore.save(any()) } @@ -704,6 +708,32 @@ class Sep6ServiceTest { ) } + @Test + fun `test withdraw from requested account`() { + val slotTxn = slot() + every { txnStore.save(capture(slotTxn)) } returns null + + val slotEvent = slot() + every { eventSession.publish(capture(slotEvent)) } returns Unit + + val request = + StartWithdrawRequest.builder() + .assetCode(TEST_ASSET) + .account("requested_account") + .refundMemo("some text") + .refundMemoType("text") + .build() + sep6Service.withdraw(TestHelper.createSep10Jwt(TEST_ACCOUNT), request) + + // Verify validations + verify(exactly = 1) { requestValidator.getWithdrawAsset(TEST_ASSET) } + verify(exactly = 1) { requestValidator.validateAccount("requested_account") } + + // Verify effects + assertEquals("requested_account", slotTxn.captured.fromAccount) + assertEquals("requested_account", slotEvent.captured.transaction.sourceAccount) + } + @Test fun `test withdraw without amount or type`() { val slotTxn = slot() @@ -722,6 +752,7 @@ class Sep6ServiceTest { // Verify validations verify(exactly = 1) { requestValidator.getWithdrawAsset(TEST_ASSET) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { txnStore.save(any()) } @@ -879,6 +910,7 @@ class Sep6ServiceTest { asset.withdraw.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { txnStore.save(any()) } @@ -932,6 +964,7 @@ class Sep6ServiceTest { asset.withdraw.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { @@ -1017,6 +1050,7 @@ class Sep6ServiceTest { asset.withdraw.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { exchangeAmountsCalculator.calculate(any(), any(), "100", TEST_ACCOUNT) } @@ -1054,6 +1088,38 @@ class Sep6ServiceTest { ) } + @Test + fun `test withdraw-exchange from requested account`() { + val sourceAsset = TEST_ASSET + val destinationAsset = "iso4217:USD" + + val slotTxn = slot() + every { txnStore.save(capture(slotTxn)) } returns null + + val slotEvent = slot() + every { eventSession.publish(capture(slotEvent)) } returns Unit + + val request = + StartWithdrawExchangeRequest.builder() + .sourceAsset(sourceAsset) + .destinationAsset(destinationAsset) + .type("bank_account") + .amount("100") + .account("requested_account") + .refundMemo("some text") + .refundMemoType("text") + .build() + sep6Service.withdrawExchange(TestHelper.createSep10Jwt(TEST_ACCOUNT), request) + + // Verify validations + verify(exactly = 1) { requestValidator.getWithdrawAsset(TEST_ASSET) } + verify(exactly = 1) { requestValidator.validateAccount("requested_account") } + + // Verify effects + assertEquals("requested_account", slotTxn.captured.fromAccount) + assertEquals("requested_account", slotEvent.captured.transaction.sourceAccount) + } + @Test fun `test withdraw-exchange with unsupported source asset`() { val unsupportedAsset = "??" @@ -1203,6 +1269,7 @@ class Sep6ServiceTest { asset.withdraw.maxAmount, ) } + verify(exactly = 1) { requestValidator.validateAccount(TEST_ACCOUNT) } // Verify effects verify(exactly = 1) { txnStore.save(any()) } diff --git a/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6End2EndTest.kt b/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6End2EndTest.kt index 365c902ece..80782dcfda 100644 --- a/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6End2EndTest.kt +++ b/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6End2EndTest.kt @@ -52,7 +52,9 @@ class Sep6End2EndTest(val config: TestConfig, val jwt: String) { val sep6Client = Sep6Client("${config.env["anchor.domain"]}/sep6", token.token) // Create a customer before starting the transaction - anchor.customer(token).add(mapOf("first_name" to "John", "last_name" to "Doe")) + anchor + .customer(token) + .add(mapOf("first_name" to "John", "last_name" to "Doe", "email_address" to "john@email.com")) val deposit = sep6Client.deposit( @@ -98,7 +100,9 @@ class Sep6End2EndTest(val config: TestConfig, val jwt: String) { val sep6Client = Sep6Client("${config.env["anchor.domain"]}/sep6", token.token) // Create a customer before starting the transaction - anchor.customer(token).add(mapOf("first_name" to "John", "last_name" to "Doe")) + anchor + .customer(token) + .add(mapOf("first_name" to "John", "last_name" to "Doe", "email_address" to "john@email.com")) val withdraw = sep6Client.withdraw( diff --git a/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6Tests.kt b/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6Tests.kt index e25229958f..7fdda74c94 100644 --- a/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6Tests.kt +++ b/integration-tests/src/test/kotlin/org/stellar/anchor/platform/test/Sep6Tests.kt @@ -2,14 +2,22 @@ package org.stellar.anchor.platform.test import org.skyscreamer.jsonassert.JSONAssert import org.skyscreamer.jsonassert.JSONCompareMode +import org.stellar.anchor.api.sep.sep12.Sep12PutCustomerRequest import org.stellar.anchor.platform.CLIENT_WALLET_ACCOUNT +import org.stellar.anchor.platform.Sep12Client import org.stellar.anchor.platform.Sep6Client import org.stellar.anchor.platform.gson import org.stellar.anchor.util.Log import org.stellar.anchor.util.Sep1Helper.TomlContent class Sep6Tests(val toml: TomlContent, jwt: String) { - private val sep6Client = Sep6Client(toml.getString("TRANSFER_SERVER"), jwt) + private val sep6Client: Sep6Client + private val sep12Client: Sep12Client + + init { + sep6Client = Sep6Client(toml.getString("TRANSFER_SERVER"), jwt) + sep12Client = Sep12Client(toml.getString("KYC_SERVER"), jwt) + } private val expectedSep6Info = """ @@ -150,8 +158,20 @@ class Sep6Tests(val toml: TomlContent, jwt: String) { ) } + private fun putCustomer() { + val request = + Sep12PutCustomerRequest.builder() + .firstName("John") + .lastName("Doe") + .emailAddress("john@email.com") + .build() + sep12Client.putCustomer(request) + } + fun testAll() { Log.info("Performing SEP6 tests") + // Create a customer before running any tests + putCustomer() `test Sep6 info endpoint`() `test sep6 deposit`() `test sep6 withdraw`() diff --git a/kotlin-reference-server/src/main/kotlin/org/stellar/reference/data/Event.kt b/kotlin-reference-server/src/main/kotlin/org/stellar/reference/data/Event.kt index 7a5d57c987..5525391d0f 100644 --- a/kotlin-reference-server/src/main/kotlin/org/stellar/reference/data/Event.kt +++ b/kotlin-reference-server/src/main/kotlin/org/stellar/reference/data/Event.kt @@ -1,5 +1,6 @@ package org.stellar.reference.data +import org.stellar.anchor.api.platform.CustomerUpdatedResponse import org.stellar.anchor.api.platform.GetQuoteResponse import org.stellar.anchor.api.platform.GetTransactionResponse @@ -10,7 +11,8 @@ data class SendEventRequest( val payload: SendEventRequestPayload ) -public data class SendEventRequestPayload( - val transaction: GetTransactionResponse, - val quote: GetQuoteResponse +data class SendEventRequestPayload( + val transaction: GetTransactionResponse?, + val quote: GetQuoteResponse?, + val customer: CustomerUpdatedResponse? ) diff --git a/kotlin-reference-server/src/main/kotlin/org/stellar/reference/event/EventService.kt b/kotlin-reference-server/src/main/kotlin/org/stellar/reference/event/EventService.kt index 27fe58c375..5ba857c1e3 100644 --- a/kotlin-reference-server/src/main/kotlin/org/stellar/reference/event/EventService.kt +++ b/kotlin-reference-server/src/main/kotlin/org/stellar/reference/event/EventService.kt @@ -26,7 +26,7 @@ class EventService { fun getEvents(txnId: String?): List { if (txnId != null) { // filter events with txnId - return receivedEvents.filter { it.payload.transaction.id == txnId } + return receivedEvents.filter { it.payload.transaction?.id == txnId } } // return all events return receivedEvents diff --git a/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java b/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java index 8728664c19..62b134a27a 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java +++ b/platform/src/main/java/org/stellar/anchor/platform/component/sep/SepBeans.java @@ -123,9 +123,10 @@ Sep6Service sep6Service( AssetService assetService, Sep6TransactionStore txnStore, EventService eventService, + CustomerIntegration customerIntegration, FeeIntegration feeIntegration, Sep38QuoteStore sep38QuoteStore) { - RequestValidator requestValidator = new RequestValidator(assetService); + RequestValidator requestValidator = new RequestValidator(assetService, customerIntegration); ExchangeAmountsCalculator exchangeAmountsCalculator = new ExchangeAmountsCalculator(feeIntegration, sep38QuoteStore, assetService); return new Sep6Service( diff --git a/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java b/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java index c801f0439a..6f0299886f 100644 --- a/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java +++ b/platform/src/main/java/org/stellar/anchor/platform/controller/AbstractControllerExceptionHandler.java @@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.stellar.anchor.api.exception.*; +import org.stellar.anchor.api.sep.CustomerInfoNeededResponse; import org.stellar.anchor.api.sep.SepExceptionResponse; public abstract class AbstractControllerExceptionHandler { @@ -41,6 +42,12 @@ public SepExceptionResponse handleAuthError(SepException ex) { return new SepExceptionResponse(ex.getMessage()); } + @ExceptionHandler(SepCustomerInfoNeededException.class) + @ResponseStatus(value = HttpStatus.FORBIDDEN) + public CustomerInfoNeededResponse handle(SepCustomerInfoNeededException ex) { + return new CustomerInfoNeededResponse(ex.getFields()); + } + @ExceptionHandler({SepNotFoundException.class, NotFoundException.class}) @ResponseStatus(value = HttpStatus.NOT_FOUND) SepExceptionResponse handleNotFound(AnchorException ex) {