Parcourir la source

WIP. free and code payment tests now passing

tags/v0.1.6
Jonathan Cobb il y a 5 ans
Parent
révision
c841ed6326
10 fichiers modifiés avec 93 ajouts et 22 suppressions
  1. +1
    -0
      bubble-server/src/main/java/bubble/ApiConstants.java
  2. +2
    -1
      bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java
  3. +1
    -1
      bubble-server/src/main/java/bubble/cloud/payment/code/CodePaymentDriver.java
  4. +1
    -1
      bubble-server/src/main/java/bubble/cloud/payment/code/CodePaymentToken.java
  5. +4
    -0
      bubble-server/src/main/java/bubble/dao/bill/AccountPaymentDAO.java
  6. +9
    -0
      bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java
  7. +44
    -1
      bubble-server/src/main/java/bubble/resources/bill/BillsResource.java
  8. +4
    -2
      bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties
  9. +24
    -13
      bubble-server/src/test/resources/models/tests/payment/pay_code.json
  10. +3
    -3
      bubble-server/src/test/resources/models/tests/payment/pay_credit.json

+ 1
- 0
bubble-server/src/main/java/bubble/ApiConstants.java Voir le fichier

@@ -122,6 +122,7 @@ public class ApiConstants {
public static final String EP_PAYMENT_METHODS = PAYMENT_METHODS_ENDPOINT;
public static final String EP_PAYMENT = "/payment";
public static final String EP_PAYMENTS = "/payments";
public static final String EP_PAY = "/pay";
public static final String EP_BILL = "/bill";
public static final String EP_BILLS = "/bills";
public static final String EP_CLOSEST = "/closest";


+ 2
- 1
bubble-server/src/main/java/bubble/cloud/payment/PaymentDriverBase.java Voir le fichier

@@ -95,6 +95,7 @@ public abstract class PaymentDriverBase<T> extends CloudServiceDriverBase<T> imp
} catch (RuntimeException e) {
// record failed payment, rethrow
accountPaymentDAO.create(new AccountPayment()
.setType(AccountPaymentType.payment)
.setAccount(accountPlan.getAccount())
.setPlan(accountPlan.getPlan())
.setAccountPlan(accountPlan.getUuid())
@@ -212,7 +213,7 @@ public abstract class PaymentDriverBase<T> extends CloudServiceDriverBase<T> imp
String refundInfo) {
// record the payment
final AccountPayment accountPayment = accountPaymentDAO.create(new AccountPayment()
.setType(AccountPaymentType.payment)
.setType(AccountPaymentType.refund)
.setAccount(accountPlan.getAccount())
.setPlan(accountPlan.getPlan())
.setAccountPlan(accountPlan.getUuid())


+ 1
- 1
bubble-server/src/main/java/bubble/cloud/payment/code/CodePaymentDriver.java Voir le fichier

@@ -151,7 +151,7 @@ public class CodePaymentDriver extends PaymentDriverBase<DefaultPaymentDriverCon
throw invalidEx("err.purchase.tokenInvalid");
}
if (cpToken.expired()) throw invalidEx("err.purchase.tokenExpired");
if (!cpToken.hasPaymentMethod(accountPlan.getUuid())) {
if (!cpToken.hasAccountPlan(accountPlan.getUuid())) {
throw invalidEx("err.purchase.tokenInvalid");
}
return cpToken.getToken();


+ 1
- 1
bubble-server/src/main/java/bubble/cloud/payment/code/CodePaymentToken.java Voir le fichier

@@ -23,7 +23,7 @@ public class CodePaymentToken {
@Getter @Setter private Long expiration;
public boolean expired() { return expiration != null && now() > expiration; }

public boolean hasPaymentMethod(String accountPlan) {
public boolean hasAccountPlan(String accountPlan) {
return this.accountPlan != null && this.accountPlan.equals(accountPlan);
}



+ 4
- 0
bubble-server/src/main/java/bubble/dao/bill/AccountPaymentDAO.java Voir le fichier

@@ -2,6 +2,7 @@ package bubble.dao.bill;

import bubble.dao.account.AccountOwnedEntityDAO;
import bubble.model.bill.AccountPayment;
import org.hibernate.criterion.Order;
import org.springframework.stereotype.Repository;

import java.util.List;
@@ -9,6 +10,9 @@ import java.util.List;
@Repository
public class AccountPaymentDAO extends AccountOwnedEntityDAO<AccountPayment> {

// newest first
@Override public Order getDefaultSortOrder() { return Order.desc("ctime"); }

public List<AccountPayment> findByAccountAndPlan(String accountUuid, String accountPlanUuid) {
return findByFields("account", accountUuid, "plan", accountPlanUuid);
}


+ 9
- 0
bubble-server/src/main/java/bubble/dao/bill/AccountPlanDAO.java Voir le fichier

@@ -8,6 +8,7 @@ import bubble.model.bill.*;
import bubble.model.cloud.BubbleNetwork;
import bubble.model.cloud.BubbleNetworkState;
import bubble.model.cloud.CloudService;
import bubble.notify.payment.PaymentValidationResult;
import bubble.server.BubbleConfiguration;
import bubble.service.bill.RefundService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -47,10 +48,18 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> {
if (!accountPlan.hasPaymentMethodObject()) throw invalidEx("err.paymentMethod.required");
if (!accountPlan.getPaymentMethodObject().hasUuid()) throw invalidEx("err.paymentMethod.required");

if (accountPlan.getPaymentMethod() == null) {
accountPlan.setPaymentMethod(accountPlan.getPaymentMethodObject().getUuid());
}

final CloudService paymentService = cloudDAO.findByUuid(accountPlan.getPaymentMethodObject().getCloud());
if (paymentService == null) throw invalidEx("err.paymentService.notFound");

final PaymentServiceDriver paymentDriver = paymentService.getPaymentDriver(configuration);
if (paymentDriver.getPaymentMethodType().requiresClaim()) {
final PaymentValidationResult result = paymentDriver.claim(accountPlan);
if (result.hasErrors()) throw invalidEx(result.violationsList());
}
if (paymentDriver.getPaymentMethodType().requiresAuth()) {
final BubblePlan plan = planDAO.findByUuid(accountPlan.getPlan());
paymentDriver.authorize(plan, accountPlan.getPaymentMethodObject());


+ 44
- 1
bubble-server/src/main/java/bubble/resources/bill/BillsResource.java Voir le fichier

@@ -1,24 +1,37 @@
package bubble.resources.bill;

import bubble.cloud.payment.PaymentServiceDriver;
import bubble.dao.bill.AccountPaymentMethodDAO;
import bubble.dao.bill.BillDAO;
import bubble.dao.cloud.CloudServiceDAO;
import bubble.model.account.Account;
import bubble.model.bill.AccountPaymentMethod;
import bubble.model.bill.AccountPlan;
import bubble.model.bill.Bill;
import bubble.model.cloud.CloudService;
import bubble.resources.account.ReadOnlyAccountOwnedResource;
import lombok.extern.slf4j.Slf4j;
import org.cobbzilla.wizard.validation.ValidationResult;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import java.util.List;

import static bubble.ApiConstants.EP_PAY;
import static bubble.ApiConstants.EP_PAYMENTS;
import static org.cobbzilla.wizard.resources.ResourceUtil.notFoundEx;
import static org.cobbzilla.wizard.resources.ResourceUtil.*;

@Slf4j
public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> {

@Autowired private AccountPaymentMethodDAO paymentMethodDAO;
@Autowired private CloudServiceDAO cloudDAO;

private AccountPlan accountPlan;

public BillsResource(Account account) { super(account); }
@@ -46,4 +59,34 @@ public class BillsResource extends ReadOnlyAccountOwnedResource<Bill, BillDAO> {
return configuration.subResource(AccountPaymentsResource.class, account, bill);
}

@POST @Path("/{id}"+EP_PAY)
public Response payBill(@Context ContainerRequest ctx,
@PathParam("id") String id,
AccountPaymentMethod paymentMethod) {
final Bill bill = super.find(ctx, id);
if (bill == null) return notFound(id);
if (bill.paid()) return invalid("err.bill.alreadyPaid");

final AccountPaymentMethod payMethodToUse;
if (paymentMethod.hasUuid()) {
payMethodToUse = paymentMethodDAO.findByUuid(paymentMethod.getUuid());
if (payMethodToUse == null) return invalid("err.paymentMethod.notFound");
} else {
final ValidationResult result = new ValidationResult();
paymentMethod.setAccount(getAccountUuid(ctx)).validate(result, configuration);
if (result.isInvalid()) return invalid(result);
payMethodToUse = paymentMethodDAO.create(paymentMethod);
}
final CloudService paymentCloud = cloudDAO.findByUuid(payMethodToUse.getCloud());
if (paymentCloud == null) return invalid("err.paymentService.notFound");

final PaymentServiceDriver paymentDriver = paymentCloud.getPaymentDriver(configuration);
if (paymentDriver.purchase(bill.getAccountPlan(), payMethodToUse.getUuid(), bill.getUuid())) {
// re-lookup bill, should now be paid
return ok(getDao().findByUuid(bill.getUuid()));
} else {
return invalid("err.purchase.declined");
}
}

}

+ 4
- 2
bubble-server/src/main/resources/message_templates/server/en_US/post_auth/ResourceMessages.properties Voir le fichier

@@ -82,6 +82,7 @@ err.authenticator.notConfigured=Authenticator has not been configured
err.backup.cannotDelete=Cannot delete backup with its current status
err.backupCleaner.didNotRun=Backup cleaner did not run
err.backupCleaner.neverRun=Backup cleaner was never run
err.bill.alreadyPaid=Bill has already been paid
err.cannotCreate=Create not allowed
err.cannotUpdate=Update not allowed
err.chargeName.required=Charge name is required
@@ -159,13 +160,14 @@ err.node.running=Node must be stopped before deleting
err.node.shutdownFailed=Node shutdown failed
err.node.stop.error=Error stopping node
err.oldPassword.invalid=Old password was invalid
err.paymentMethod.required=Payment method is required
err.paymentMethod.cannotDeleteInUse=Cannot delete payment method that is currently in use
err.paymentInfo.invalid=Payment information is invalid
err.paymentInfo.required=Payment information is required
err.paymentInfo.processingError=Processing payment information failed
err.paymentInfo.emailRequired=To use this payment method, please add an email address to your account
err.paymentInfo.verifiedEmailRequired=To use this payment method, please verify your email address
err.paymentMethod.required=Payment method is required
err.paymentMethod.notFound=Payment method not found
err.paymentMethod.cannotDeleteInUse=Cannot delete payment method that is currently in use
err.paymentMethodInfo.invalid=Payment method information is invalid
err.paymentMethodType.required=Payment method type is required
err.paymentMethodType.mismatch=Payment method type mismatch


+ 24
- 13
bubble-server/src/test/resources/models/tests/payment/pay_code.json Voir le fichier

@@ -89,6 +89,7 @@
},

{
"before": "sleep 15s",
"comment": "verify account payment methods, should be one",
"request": { "uri": "me/paymentMethods" },
"response": {
@@ -148,18 +149,28 @@
},

{
"comment": "check payments for plan, expect one failed payment"
},

{
"comment": "pay for plan with a different code, succeeds"
},

{
"comment": "check payments for plan, expect two payments, one failed, the second successful"
},

{
"comment": "try to add plan with expired code, expect error"
"comment": "try to add plan with expired code, expect error",
"request": {
"uri": "me/plans",
"method": "put",
"entity": {
"name": "test-net3-{{rand 5}}",
"domain": "{{defaultDomain}}",
"locale": "en_US",
"timezone": "EST",
"plan": "{{plans.[0].name}}",
"footprint": "US",
"paymentMethodObject": {
"paymentMethodType": "code",
"paymentInfo": "expired_invite_token"
}
}
},
"response": {
"status": 422,
"check": [
{"condition": "json.has('err.purchase.tokenExpired')"}
]
}
}
]

+ 3
- 3
bubble-server/src/test/resources/models/tests/payment/pay_credit.json Voir le fichier

@@ -60,7 +60,7 @@
"timezone": "EST",
"plan": "{{plans.[0].name}}",
"footprint": "US",
"paymentMethod": {
"paymentMethodObject": {
"paymentMethodType": "credit",
"paymentInfo": "invalid_token"
}
@@ -86,7 +86,7 @@
"timezone": "EST",
"plan": "{{plans.[0].name}}",
"footprint": "US",
"paymentMethod": {
"paymentMethodObject": {
"paymentMethodType": "credit",
"paymentInfo": "{{stripeToken}}"
}
@@ -138,7 +138,7 @@
"timezone": "EST",
"plan": "{{plans.[0].name}}",
"footprint": "US",
"paymentMethod": {
"paymentMethodObject": {
"paymentMethodType": "credit",
"paymentInfo": "{{stripeToken}}"
}


Chargement…
Annuler
Enregistrer