@@ -85,7 +85,7 @@ public abstract class PaymentDriverBase<T> extends CloudServiceDriverBase<T> imp | |||
final AccountPlanPayment priorPayment = findPriorPayment(plan, paymentMethod, bill); | |||
if (priorPayment != null) { | |||
billDAO.update(bill.setStatus(BillStatus.paid)); | |||
billDAO.update(bill.setPrice(0L).setStatus(BillStatus.paid)); | |||
accountPlanPaymentDAO.create(new AccountPlanPayment() | |||
.setAccount(accountPlan.getAccount()) | |||
.setPlan(accountPlan.getPlan()) | |||
@@ -129,14 +129,15 @@ public abstract class PaymentDriverBase<T> extends CloudServiceDriverBase<T> imp | |||
// - it is for the current period | |||
// - the corresponding AccountPlan has been deleted | |||
// if so we can re-use that payment and do not need to charge anything now | |||
final List<AccountPlanPayment> priorSimilarPayments = accountPlanPaymentDAO.findByAccountPaymentMethodAndPeriodAndPriceAndCurrency( | |||
final List<AccountPlanPayment> priorSimilarPayments = accountPlanPaymentDAO.findByAccountPaymentMethodAndPeriodAndCurrency( | |||
paymentMethod.getUuid(), | |||
bill.getPeriod(), | |||
plan.getPrice(), | |||
plan.getCurrency()); | |||
for (AccountPlanPayment app : priorSimilarPayments) { | |||
final AccountPlan ap = accountPlanDAO.findByUuid(app.getAccountPlan()); | |||
if (ap != null && ap.deleted()) return app; | |||
if (ap != null && ap.deleted() && app.getAmount() >= plan.getPrice()) { | |||
return app; | |||
} | |||
} | |||
return null; | |||
} | |||
@@ -2,13 +2,18 @@ package bubble.dao.bill; | |||
import bubble.cloud.payment.PaymentServiceDriver; | |||
import bubble.dao.account.AccountOwnedEntityDAO; | |||
import bubble.dao.cloud.BubbleNetworkDAO; | |||
import bubble.dao.cloud.CloudServiceDAO; | |||
import bubble.model.bill.*; | |||
import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.cloud.BubbleNetworkState; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.server.BubbleConfiguration; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
import static java.util.concurrent.TimeUnit.SECONDS; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.background; | |||
import static org.cobbzilla.util.system.Sleep.sleep; | |||
@@ -21,12 +26,17 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> { | |||
@Autowired private BubblePlanDAO planDAO; | |||
@Autowired private BillDAO billDAO; | |||
@Autowired private CloudServiceDAO cloudDAO; | |||
@Autowired private BubbleNetworkDAO networkDAO; | |||
@Autowired private BubbleConfiguration configuration; | |||
public AccountPlan findByAccountAndNetwork(String accountUuid, String networkUuid) { | |||
return findByUniqueFields("account", accountUuid, "network", networkUuid); | |||
} | |||
public List<AccountPlan> findByAccountAndNotDeleted(String account) { | |||
return findByFields("account", account, "deleted", false); | |||
} | |||
@Override public Object preCreate(AccountPlan accountPlan) { | |||
if (!accountPlan.hasPaymentMethod()) throw invalidEx("err.paymentMethod.required"); | |||
@@ -74,4 +84,17 @@ public class AccountPlanDAO extends AccountOwnedEntityDAO<AccountPlan> { | |||
return super.postCreate(accountPlan, context); | |||
} | |||
@Override public void delete(String uuid) { | |||
final AccountPlan accountPlan = findByUuid(uuid); | |||
if (accountPlan == null) return; | |||
final BubbleNetwork network = networkDAO.findByUuid(accountPlan.getNetwork()); | |||
if (network != null && network.getState() != BubbleNetworkState.stopped) { | |||
throw invalidEx("err.accountPlan.stopNetworkBeforeDeleting"); | |||
} | |||
update(accountPlan.setDeleted(true).setEnabled(false)); | |||
} | |||
@Override public void forceDelete(String uuid) { super.delete(uuid); } | |||
} |
@@ -6,8 +6,6 @@ import org.springframework.stereotype.Repository; | |||
import java.util.List; | |||
import static org.hibernate.criterion.Restrictions.*; | |||
@Repository | |||
public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO<AccountPlanPayment> { | |||
@@ -17,16 +15,14 @@ public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO<AccountPlanPaym | |||
return findByUniqueFields("planPaymentMethod", planPaymentMethod, "bill", billUuid); | |||
} | |||
public List<AccountPlanPayment> findByAccountPaymentMethodAndPeriodAndPriceAndCurrency(String paymentMethodUuid, | |||
String billPeriod, | |||
Long price, | |||
String currency) { | |||
return list(criteria().add(and( | |||
eq("paymentMethod", paymentMethodUuid), | |||
eq("period", billPeriod), | |||
eq("currency", currency), | |||
ge("amount", price) | |||
))); | |||
public List<AccountPlanPayment> findByAccountPayment(String accountPayment) { | |||
return findByField("payment", accountPayment); | |||
} | |||
public List<AccountPlanPayment> findByAccountPaymentMethodAndPeriodAndCurrency(String paymentMethodUuid, | |||
String billPeriod, | |||
String currency) { | |||
return findByFields("paymentMethod", paymentMethodUuid, "period", billPeriod, "currency", currency); | |||
} | |||
public List<AccountPlanPayment> findByAccountAndBill(String accountUuid, String billUuid) { | |||
@@ -40,4 +36,5 @@ public class AccountPlanPaymentDAO extends AccountOwnedEntityDAO<AccountPlanPaym | |||
public List<AccountPlanPayment> findByAccountAndAccountPlanAndBill(String accountUuid, String accountPlanUuid, String billUuid) { | |||
return findByFields("account", accountUuid, "accountPlan", accountPlanUuid, "bill", billUuid); | |||
} | |||
} |
@@ -3,7 +3,10 @@ package bubble.dao.cloud; | |||
import bubble.dao.account.AccountOwnedEntityDAO; | |||
import bubble.dao.bill.AccountPlanDAO; | |||
import bubble.model.bill.AccountPlan; | |||
import bubble.model.cloud.*; | |||
import bubble.model.cloud.BubbleDomain; | |||
import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.cloud.BubbleNode; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.server.BubbleConfiguration; | |||
import bubble.service.boot.SelfNodeService; | |||
import bubble.service.cloud.NetworkService; | |||
@@ -16,8 +19,6 @@ import java.io.IOException; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.invalidEx; | |||
@Repository @Slf4j | |||
public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | |||
@@ -64,12 +65,11 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | |||
public void delete(String uuid, boolean force) { | |||
final BubbleNetwork network = findByUuid(uuid); | |||
if (network.getState() != BubbleNetworkState.stopped) { | |||
if (force) { | |||
networkService.stopNetwork(network); | |||
} else { | |||
throw invalidEx("err.network.cannotDelete", "stop network before deleting"); | |||
} | |||
if (network == null) return; | |||
try { | |||
networkService.stopNetwork(network); | |||
} catch (Exception e) { | |||
log.warn("delete("+uuid+"): error stopping network: "+e); | |||
} | |||
try { | |||
if (force) { | |||
@@ -104,11 +104,10 @@ public class BubbleNetworkDAO extends AccountOwnedEntityDAO<BubbleNetwork> { | |||
if (accountPlan == null) { | |||
log.warn("delete("+uuid+"): AccountPlan not found"); | |||
} else { | |||
if (force) { | |||
accountPlanDAO.delete(accountPlan.getUuid()); | |||
} else { | |||
accountPlanDAO.update(accountPlan.setNetwork(null).setDeletedNetwork(network.getUuid())); | |||
} | |||
accountPlanDAO.update(accountPlan | |||
.setEnabled(false) | |||
.setNetwork(null) | |||
.setDeletedNetwork(network.getUuid())); | |||
} | |||
if (force) { | |||
@@ -23,10 +23,7 @@ import org.cobbzilla.wizard.model.entityconfig.annotations.ECType; | |||
import org.cobbzilla.wizard.validation.ValidationResult; | |||
import org.hibernate.annotations.Type; | |||
import javax.persistence.Column; | |||
import javax.persistence.Entity; | |||
import javax.persistence.EnumType; | |||
import javax.persistence.Enumerated; | |||
import javax.persistence.*; | |||
import static org.cobbzilla.util.daemon.ZillaRuntime.empty; | |||
import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
@@ -31,7 +31,8 @@ import static org.cobbzilla.util.reflect.ReflectionUtil.copy; | |||
public class AccountPlan extends IdentifiableBase implements HasAccount { | |||
public static final String[] CREATE_FIELDS = { | |||
"name", "description", "locale", "timezone", "domain", "network", "plan", "footprint", "paymentMethod" | |||
"name", "description", "locale", "timezone", "domain", "network", "plan", "footprint", | |||
"paymentMethod", "savedPaymentMethod" | |||
}; | |||
@SuppressWarnings("unused") | |||
@@ -62,9 +63,14 @@ public class AccountPlan extends IdentifiableBase implements HasAccount { | |||
@Getter @Setter private Boolean enabled = false; | |||
public boolean enabled() { return enabled != null && enabled; } | |||
@Column(nullable=false) | |||
@Getter @Setter private Boolean deleted = false; | |||
public boolean deleted() { return deleted != null && deleted; } | |||
public boolean notDeleted() { return !deleted(); } | |||
@ECIndex(unique=true) @Column(length=UUID_MAXLEN) | |||
@Getter @Setter private String deletedNetwork; | |||
public boolean deleted() { return deletedNetwork != null; } | |||
public boolean hasDeletedNetwork() { return deletedNetwork != null; } | |||
// Fields below are used when creating a new plan, to also create the network associated with it | |||
@Size(max=10000, message="err.description.length") | |||
@@ -50,7 +50,7 @@ public class Bill extends IdentifiableBase implements HasAccountNoName { | |||
@Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Getter @Setter private Long quantity = 0L; | |||
@Type(type=ENCRYPTED_LONG) @Column(updatable=false, columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Type(type=ENCRYPTED_LONG) @Column(columnDefinition="varchar("+(ENC_LONG)+") NOT NULL") | |||
@Getter @Setter private Long price = 0L; | |||
@ECIndex @Column(nullable=false, updatable=false, length=10) | |||
@@ -1,21 +1,55 @@ | |||
package bubble.resources.bill; | |||
import bubble.dao.bill.AccountPaymentDAO; | |||
import bubble.dao.bill.AccountPlanPaymentDAO; | |||
import bubble.dao.bill.BillDAO; | |||
import bubble.model.account.Account; | |||
import bubble.model.bill.AccountPayment; | |||
import bubble.model.bill.AccountPlanPayment; | |||
import bubble.model.bill.Bill; | |||
import bubble.resources.account.ReadOnlyAccountOwnedResource; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import javax.ws.rs.Consumes; | |||
import javax.ws.rs.Produces; | |||
import javax.ws.rs.*; | |||
import javax.ws.rs.core.Context; | |||
import javax.ws.rs.core.Response; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import static bubble.ApiConstants.EP_BILLS; | |||
import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.notFound; | |||
import static org.cobbzilla.wizard.resources.ResourceUtil.ok; | |||
@Consumes(APPLICATION_JSON) | |||
@Produces(APPLICATION_JSON) | |||
@Slf4j | |||
public class AccountPaymentsResource extends ReadOnlyAccountOwnedResource<AccountPayment, AccountPaymentDAO> { | |||
@Autowired private AccountPlanPaymentDAO planPaymentDAO; | |||
@Autowired private BillDAO billDAO; | |||
public AccountPaymentsResource(Account account) { super(account); } | |||
@GET @Path("/{id}"+EP_BILLS) | |||
public Response getBills(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPayment accountPayment = super.find(ctx, id); | |||
if (accountPayment == null) return notFound(id); | |||
final List<AccountPlanPayment> planPayments = planPaymentDAO.findByAccountPayment(accountPayment.getUuid()); | |||
final List<Bill> bills = new ArrayList<>(); | |||
for (AccountPlanPayment app : planPayments) { | |||
final Bill bill = billDAO.findByUuid(app.getBill()); | |||
if (bill == null) { | |||
log.warn("getBills: bill "+app.getBill()+" not found for AccountPlanPayment="+app.getUuid()); | |||
continue; | |||
} | |||
bills.add(bill); | |||
} | |||
return ok(bills); | |||
} | |||
} |
@@ -14,6 +14,7 @@ import lombok.extern.slf4j.Slf4j; | |||
import org.glassfish.jersey.server.ContainerRequest; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import javax.ws.rs.GET; | |||
import javax.ws.rs.Path; | |||
import javax.ws.rs.PathParam; | |||
import javax.ws.rs.core.Context; | |||
@@ -75,7 +76,7 @@ public class AccountPlanPaymentsResource extends ReadOnlyAccountOwnedResource<Ac | |||
return planPayment; | |||
} | |||
@Path("/{id}"+EP_PAYMENT) | |||
@GET @Path("/{id}"+EP_PAYMENT) | |||
public Response getPayment(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlanPayment planPayment = super.find(ctx, id); | |||
@@ -84,7 +85,7 @@ public class AccountPlanPaymentsResource extends ReadOnlyAccountOwnedResource<Ac | |||
return payment == null ? notFound(id) : ok(payment); | |||
} | |||
@Path("/{id}"+EP_BILL) | |||
@GET @Path("/{id}"+EP_BILL) | |||
public Response getBill(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlanPayment planPayment = super.find(ctx, id); | |||
@@ -15,10 +15,7 @@ import bubble.model.bill.AccountPaymentMethod; | |||
import bubble.model.bill.AccountPlan; | |||
import bubble.model.bill.AccountPlanPaymentMethod; | |||
import bubble.model.bill.BubblePlan; | |||
import bubble.model.cloud.BubbleDomain; | |||
import bubble.model.cloud.BubbleFootprint; | |||
import bubble.model.cloud.BubbleNetwork; | |||
import bubble.model.cloud.CloudService; | |||
import bubble.model.cloud.*; | |||
import bubble.resources.account.AccountOwnedResource; | |||
import bubble.server.BubbleConfiguration; | |||
import lombok.extern.slf4j.Slf4j; | |||
@@ -53,33 +50,13 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
public AccountPlansResource(Account account) { super(account); } | |||
@Path("/{id}"+EP_BILLS) | |||
public BillsResource getBills(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) throw notFoundEx(id); | |||
return configuration.subResource(BillsResource.class, account, plan); | |||
@Override protected AccountPlan find(ContainerRequest ctx, String id) { | |||
final AccountPlan accountPlan = super.find(ctx, id); | |||
return accountPlan == null || accountPlan.deleted() ? null : accountPlan; | |||
} | |||
@Path("/{id}"+EP_PAYMENTS) | |||
public AccountPlanPaymentsResource getPayments(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) throw notFoundEx(id); | |||
return configuration.subResource(AccountPlanPaymentsResource.class, account, plan); | |||
} | |||
@GET @Path("/{id}"+EP_PAYMENT_METHOD) | |||
public Response getPaymentMethod(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) return notFound(id); | |||
final AccountPlanPaymentMethod planPaymentMethod = accountPlanPaymentMethodDAO.findCurrentMethodForPlan(plan.getUuid()); | |||
if (planPaymentMethod == null) return notFound(); | |||
final AccountPaymentMethod paymentMethod = accountPaymentMethodDAO.findByUuid(planPaymentMethod.getPaymentMethod()); | |||
return paymentMethod == null ? notFound() : ok(paymentMethod); | |||
@Override protected List<AccountPlan> list(ContainerRequest ctx) { | |||
return getDao().findByAccountAndNotDeleted(account.getUuid()); | |||
} | |||
@Override protected boolean canCreate(Request req, ContainerRequest ctx, Account caller, AccountPlan request) { | |||
@@ -166,6 +143,8 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
final AccountPaymentMethod paymentMethodToCreate = new AccountPaymentMethod(request.getPaymentMethod()).setAccount(request.getAccount()); | |||
final AccountPaymentMethod paymentMethodCreated = accountPaymentMethodDAO.create(paymentMethodToCreate); | |||
request.setPaymentMethod(paymentMethodCreated); | |||
} else { | |||
request.setPaymentMethod(paymentMethod); | |||
} | |||
return request; | |||
} | |||
@@ -191,4 +170,33 @@ public class AccountPlansResource extends AccountOwnedResource<AccountPlan, Acco | |||
} | |||
} | |||
@Path("/{id}"+EP_BILLS) | |||
public BillsResource getBills(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) throw notFoundEx(id); | |||
return configuration.subResource(BillsResource.class, account, plan); | |||
} | |||
@Path("/{id}"+EP_PAYMENTS) | |||
public AccountPlanPaymentsResource getPayments(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) throw notFoundEx(id); | |||
return configuration.subResource(AccountPlanPaymentsResource.class, account, plan); | |||
} | |||
@GET @Path("/{id}"+EP_PAYMENT_METHOD) | |||
public Response getPaymentMethod(@Context ContainerRequest ctx, | |||
@PathParam("id") String id) { | |||
final AccountPlan plan = find(ctx, id); | |||
if (plan == null) return notFound(id); | |||
final AccountPlanPaymentMethod planPaymentMethod = accountPlanPaymentMethodDAO.findCurrentMethodForPlan(plan.getUuid()); | |||
if (planPaymentMethod == null) return notFound(); | |||
final AccountPaymentMethod paymentMethod = accountPaymentMethodDAO.findByUuid(planPaymentMethod.getPaymentMethod()); | |||
return paymentMethod == null ? notFound() : ok(paymentMethod); | |||
} | |||
} |
@@ -73,6 +73,7 @@ err.accountContactsJson.length=Account contacts length violation | |||
err.accountPlan.callerCountryDisallowed=Your country is not currently supported by our platform | |||
err.accountPlan.disabled=Account plan is not enabled | |||
err.accountPlan.notFound=Account plan not found | |||
err.accountPlan.stopNetworkBeforeDeleting=You must stop the bubble first, the delete the plan | |||
err.admin.cannotRemoveAdminStatusFromSelf=You cannot remove admin status from your own account | |||
err.allowedCountriesJson.length=Allowed countries list is too long | |||
err.approval.invalid=Approval cannot proceed | |||
@@ -145,7 +146,6 @@ err.name.mismatch=Name mismatch | |||
err.name.reserved=Name is reserved | |||
err.netlocation.invalid=Must specify both cloud and region, or neither | |||
err.network.alreadyStarted=Network is already started | |||
err.network.cannotDelete=CAnnot delete network: stop network before deleting | |||
err.network.exists=A plan already exists for this network | |||
err.networkKeys.noVerifiedContacts=No verified contacts exist | |||
err.networkName.required=Network name is required | |||
@@ -98,6 +98,7 @@ | |||
}, | |||
{ | |||
"before": "sleep 15s", | |||
"comment": "verify account payment methods, should be one", | |||
"request": { "uri": "me/paymentMethods" }, | |||
"response": { | |||
@@ -114,7 +115,8 @@ | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getName() === accountPlan.getName()"} | |||
{"condition": "json[0].getName() === accountPlan.getName()"}, | |||
{"condition": "json[0].enabled()"} | |||
] | |||
} | |||
}, | |||
@@ -123,7 +125,7 @@ | |||
"comment": "verify account plan payment info", | |||
"request": { "uri": "me/plans/{{accountPlan.uuid}}/paymentMethod" }, | |||
"response": { | |||
"store": "savedPaymentMethods", | |||
"store": "savedPaymentMethod", | |||
"check": [ | |||
{"condition": "json.getPaymentMethodType().name() === 'credit'"}, | |||
{"condition": "json.getMaskedPaymentInfo() === 'XXXX-XXXX-XXXX-4242'"} | |||
@@ -138,7 +140,7 @@ | |||
"store": "bills", | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{accountPlan.uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{accountPlan.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getTotal() === {{plans.[0].price}}"}, | |||
@@ -154,8 +156,8 @@ | |||
"store": "payments", | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{accountPlan.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === '{{plans.[0].price}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{accountPlan.uuid}}'"}, | |||
{"condition": "json[0].getAmount() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"} | |||
] | |||
} | |||
@@ -172,6 +174,26 @@ | |||
} | |||
}, | |||
{ | |||
"comment": "try to delete plan, must stop network first", | |||
"request": { | |||
"method": "delete", | |||
"uri": "me/plans/{{accountPlan.uuid}}" | |||
}, | |||
"response": { | |||
"status": 422, | |||
"check": [{"condition": "json.has('err.accountPlan.stopNetworkBeforeDeleting')"}] | |||
} | |||
}, | |||
{ | |||
"comment": "delete network", | |||
"request": { | |||
"method": "delete", | |||
"uri": "me/networks/{{accountPlan.network}}" | |||
} | |||
}, | |||
{ | |||
"comment": "delete plan", | |||
"request": { | |||
@@ -201,8 +223,7 @@ | |||
"plan": "{{plans.[0].name}}", | |||
"footprint": "US", | |||
"paymentMethod": { | |||
"paymentMethodType": "credit", | |||
"uuid": "{{savedPaymentMethods.[0].uuid}}" | |||
"uuid": "{{savedPaymentMethod.uuid}}" | |||
} | |||
} | |||
}, | |||
@@ -212,6 +233,7 @@ | |||
}, | |||
{ | |||
"before": "sleep 15s", | |||
"comment": "verify we still have only one payment method", | |||
"request": { "uri": "me/paymentMethods" }, | |||
"response": { | |||
@@ -221,12 +243,12 @@ | |||
{ | |||
"comment": "verify account plan payment info is same as used before", | |||
"request": { "uri": "me/plans/{{accountPlan.uuid}}/paymentMethod" }, | |||
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/paymentMethod" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.getPaymentMethodType().name() === 'credit'"}, | |||
{"condition": "json.getMaskedPaymentInfo() === 'XXXX-XXXX-XXXX-4242'"}, | |||
{"condition": "json.getUuid() === '{{savedPaymentMethods.[0].uuid}}'"} | |||
{"condition": "json.getUuid() === '{{savedPaymentMethod.uuid}}'"} | |||
] | |||
} | |||
}, | |||
@@ -248,7 +270,8 @@ | |||
"store": "plan2bills", | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === 0"}, | |||
{"condition": "json[0].getTotal() === 0"} | |||
@@ -257,40 +280,48 @@ | |||
}, | |||
{ | |||
"comment": "verify we have made two successful payments (but one of them will be zero)", | |||
"comment": "verify we have still only made one successful payment", | |||
"request": { "uri": "me/payments" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 2"}, | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getStatus().name() === 'success'"}, | |||
{"condition": "json[1].getStatus().name() === 'success'"}, | |||
{"condition": "_find(json, function (p) { if (p.getAmount() === {{plans.[0].price}}) return p; }) != null"}, | |||
{"condition": "_find(json, function (p) { if (p.getAmount() === 0) return p; }) != null"} | |||
{"condition": "json[0].getAmount() === {{plans.[0].price}}"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "find payments made on second plan", | |||
"comment": "verify payment exists via plan and is successful, and price was zero", | |||
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/payments" }, | |||
"response": { | |||
"store": "plan2payments", | |||
"check": [ | |||
{"condition": "json.length === 1"} | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getAccountPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json[0].getPaymentObject().getAmount() === {{plans.[0].price}}"}, | |||
{"condition": "json[0].getPaymentObject().getStatus().name() === 'success'"}, | |||
{"condition": "json[0].getBillObject().getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getAccountPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json[0].getBillObject().getQuantity() === 1"}, | |||
{"condition": "json[0].getBillObject().getPrice() === 0"}, | |||
{"condition": "json[0].getBillObject().getTotal() === 0"}, | |||
{"condition": "json[0].getBillObject().getStatus().name() === 'paid'"} | |||
] | |||
} | |||
}, | |||
{ | |||
"comment": "verify successful payment has paid for the second bill (with zero total)", | |||
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/payments/{{plan2payments.[0].uuid}}/bills" }, | |||
"request": { "uri": "me/plans/{{accountPlan2.uuid}}/payments/{{plan2payments.[0].uuid}}/bill" }, | |||
"response": { | |||
"check": [ | |||
{"condition": "json.length === 1"}, | |||
{"condition": "json[0].getPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json[0].getQuantity() === 1"}, | |||
{"condition": "json[0].getPrice() === 0"}, | |||
{"condition": "json[0].getTotal() === 0"} | |||
{"condition": "json.getPlan() === '{{plans.[0].uuid}}'"}, | |||
{"condition": "json.getAccountPlan() === '{{accountPlan2.uuid}}'"}, | |||
{"condition": "json.getQuantity() === 1"}, | |||
{"condition": "json.getPrice() === 0"}, | |||
{"condition": "json.getTotal() === 0"} | |||
] | |||
} | |||
} | |||