Updates
This commit is contained in:
@@ -9,12 +9,14 @@ import com.test.payment.dto.PaymentRequest;
|
||||
import com.test.payment.dto.PaymentResultDto;
|
||||
import com.test.payment.dto.TransactionStatusDto;
|
||||
import com.test.payment.exceptions.ProviderBusyException;
|
||||
import com.test.payment.models.Country;
|
||||
import com.test.payment.models.Operator;
|
||||
import com.test.payment.models.PaymentProviderType;
|
||||
import com.test.payment.models.PaymentResponse;
|
||||
import com.test.payment.models.TransactionStatus;
|
||||
import com.test.payment.models.Status;
|
||||
import com.test.payment.service.PaymentLifecycleService.CallbackData;
|
||||
import com.test.payment.service.PaymentLifecycleService.ProviderResponseData;
|
||||
import com.test.payment.service.PaymentLifecycleService.QueryOutcome;
|
||||
import com.test.payment.service.PaymentLifecycleService.StoredResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -35,13 +37,16 @@ public class AirtelService implements PaymentProviderService {
|
||||
|
||||
private final AirtelClient airtelClient;
|
||||
private final PaymentLifecycleService lifecycle;
|
||||
private final ProviderMarkets markets;
|
||||
private final ProviderCallAudit audit;
|
||||
private final StatusCatalog statuses;
|
||||
private final PaymentLimitService limits;
|
||||
private final Environment environment;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public String provider() {
|
||||
return PaymentProviderType.AIRTEL.name();
|
||||
public PaymentProviderType provider() {
|
||||
return markets.resolve(Operator.AIRTEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,7 +55,13 @@ public class AirtelService implements PaymentProviderService {
|
||||
.then(lifecycle.saveInitiation(provider(), request))
|
||||
.flatMap(initiation -> Mono.defer(() -> {
|
||||
String reference = "ATL" + UUID.randomUUID().toString().replace("-", "");
|
||||
return airtelClient.pay(buildRequest(request, reference))
|
||||
AirtelPaymentRequestDto payload = buildRequest(request, reference);
|
||||
ProviderCallAudit.Handle call = audit.begin(provider(), "USSD_PUSH", String.valueOf(initiation.getId()),
|
||||
"POST", baseUrl() + "/merchant/v1/payments/", payload);
|
||||
audit.linkReference(call, reference);
|
||||
return airtelClient.pay(payload)
|
||||
.doOnNext(response -> audit.complete(call, 200, response, null))
|
||||
.doOnError(ex -> audit.complete(call, null, null, ex))
|
||||
.map(response -> toResponseData(response, reference));
|
||||
})
|
||||
.flatMap(data -> lifecycle.persistResponse(initiation, data))
|
||||
@@ -71,7 +82,11 @@ public class AirtelService implements PaymentProviderService {
|
||||
transaction.getAirtelMoneyId(),
|
||||
null, null, null,
|
||||
success);
|
||||
return lifecycle.applyCallback(provider(), data, toJson(payload));
|
||||
String raw = toJson(payload);
|
||||
audit.recordCallback(provider(), new ProviderCallAudit.CallbackAudit(
|
||||
data.providerReference(), data.resultCode(), data.resultDesc(), data.receiptNumber(),
|
||||
data.amount(), data.phoneNumber(), data.transactionDate()), raw);
|
||||
return lifecycle.applyCallback(provider(), data, raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,8 +94,12 @@ public class AirtelService implements PaymentProviderService {
|
||||
return lifecycle.checkStatus(provider(), providerReference, this::queryProvider);
|
||||
}
|
||||
|
||||
private Mono<QueryOutcome> queryProvider(PaymentResponse response) {
|
||||
return airtelClient.status(response.getProviderReference())
|
||||
private Mono<QueryOutcome> queryProvider(StoredResponse response) {
|
||||
ProviderCallAudit.Handle call = audit.begin(provider(), "STATUS_QUERY", String.valueOf(response.initiationId()),
|
||||
"GET", baseUrl() + "/standard/v1/payments/" + response.providerReference(), null);
|
||||
return airtelClient.status(response.providerReference())
|
||||
.doOnNext(result -> audit.complete(call, 200, result, null))
|
||||
.doOnError(ex -> audit.complete(call, null, null, ex))
|
||||
.map(result -> {
|
||||
AirtelResponseDto.Transaction tx = result.getData() != null ? result.getData().getTransaction() : null;
|
||||
String status = tx != null ? tx.getStatus() : null;
|
||||
@@ -91,17 +110,22 @@ public class AirtelService implements PaymentProviderService {
|
||||
tx != null ? tx.getAirtelMoneyId() : null);
|
||||
})
|
||||
.onErrorResume(ProviderBusyException.class,
|
||||
e -> Mono.just(QueryOutcome.pending("Airtel status query rate-limited — showing last known state")));
|
||||
e -> Mono.just(QueryOutcome.pending(statuses.pending(), "Airtel status query rate-limited — showing last known state")));
|
||||
}
|
||||
|
||||
private String baseUrl() {
|
||||
return environment.getProperty("airtel.base-url", "");
|
||||
}
|
||||
|
||||
private AirtelPaymentRequestDto buildRequest(PaymentRequest request, String reference) {
|
||||
String country = environment.getProperty("airtel.country", "KE");
|
||||
String currency = environment.getProperty("airtel.currency", "KES");
|
||||
Country country = markets.resolve(Operator.AIRTEL).country();
|
||||
// the market's own currency unless airtel.currency deliberately overrides it
|
||||
String currency = environment.getProperty("airtel.currency", country.currency());
|
||||
return new AirtelPaymentRequestDto(
|
||||
request.getAccountReference(),
|
||||
new AirtelPaymentRequestDto.Subscriber(country, currency, request.getPhoneNumber()),
|
||||
new AirtelPaymentRequestDto.Subscriber(country.name(), currency, request.getPhoneNumber()),
|
||||
new AirtelPaymentRequestDto.Transaction(
|
||||
String.valueOf(request.getAmount()), country, currency, reference));
|
||||
String.valueOf(request.getAmount()), country.name(), currency, reference));
|
||||
}
|
||||
|
||||
private ProviderResponseData toResponseData(AirtelResponseDto response, String reference) {
|
||||
@@ -119,14 +143,14 @@ public class AirtelService implements PaymentProviderService {
|
||||
accepted);
|
||||
}
|
||||
|
||||
private TransactionStatus mapStatus(String airtelStatus) {
|
||||
private Status mapStatus(String airtelStatus) {
|
||||
if ("TS".equalsIgnoreCase(airtelStatus)) {
|
||||
return TransactionStatus.SUCCESS;
|
||||
return statuses.success();
|
||||
}
|
||||
if ("TF".equalsIgnoreCase(airtelStatus)) {
|
||||
return TransactionStatus.FAILED;
|
||||
return statuses.failed();
|
||||
}
|
||||
return TransactionStatus.PENDING; // TIP or unknown — keep waiting
|
||||
return statuses.pending(); // TIP or unknown — keep waiting
|
||||
}
|
||||
|
||||
private String toJson(Object value) {
|
||||
|
||||
Reference in New Issue
Block a user