mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-26 07:26:41 +00:00
Payment: add comments, format the code
This commit is contained in:
+11
-3
@@ -5,19 +5,27 @@ import com.google.gwt.resources.client.ClientBundle;
|
||||
import com.google.gwt.resources.client.CssResource;
|
||||
|
||||
public interface SubscriptionProfileDesktopResources extends ClientBundle {
|
||||
public static final SubscriptionProfileDesktopResources INSTANCE = GWT.create(SubscriptionProfileDesktopResources.class);
|
||||
|
||||
public static final SubscriptionProfileDesktopResources INSTANCE = GWT
|
||||
.create(SubscriptionProfileDesktopResources.class);
|
||||
|
||||
@Source("SubscriptionProfiles.gss")
|
||||
SubscriptionProfileCss css();
|
||||
|
||||
|
||||
public interface SubscriptionProfileCss extends CssResource {
|
||||
String bottomButton();
|
||||
|
||||
String trialText();
|
||||
|
||||
String textRow();
|
||||
|
||||
String errorText();
|
||||
|
||||
String blueText();
|
||||
|
||||
String plansLabel();
|
||||
|
||||
String plansInput();
|
||||
|
||||
String planInputContainer();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -8,18 +8,18 @@ import com.sap.sailing.gwt.home.shared.places.user.profile.subscription.UserSubs
|
||||
import com.sap.sse.security.ui.authentication.app.AuthenticationContext;
|
||||
|
||||
public class UserProfileSubscriptionPresenter implements UserProfileSubscriptionView.Presenter {
|
||||
|
||||
|
||||
private final UserProfileSubscriptionView view;
|
||||
private final UserProfileView.Presenter userProfilePresenter;
|
||||
private final UserSubscriptionView.Presenter userSubscriptionPresenter;
|
||||
|
||||
|
||||
public UserProfileSubscriptionPresenter(final UserProfileSubscriptionView view,
|
||||
final UserProfileView.Presenter userProfilePresenter) {
|
||||
this.view = view;
|
||||
this.userProfilePresenter = userProfilePresenter;
|
||||
this.userSubscriptionPresenter = new UserSubscriptionPresenter<UserProfileClientFactory>(
|
||||
userProfilePresenter.getClientFactory());
|
||||
view.setPresenter(this);
|
||||
this.view = view;
|
||||
this.userProfilePresenter = userProfilePresenter;
|
||||
this.userSubscriptionPresenter = new UserSubscriptionPresenter<UserProfileClientFactory>(
|
||||
userProfilePresenter.getClientFactory());
|
||||
view.setPresenter(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -9,14 +9,14 @@ import com.sap.sailing.gwt.home.shared.ExperimentalFeatures;
|
||||
import com.sap.sailing.gwt.home.shared.places.user.profile.subscription.UserProfileSubscriptionPlace;
|
||||
import com.sap.sse.security.ui.authentication.app.AuthenticationContext;
|
||||
|
||||
public class UserProfileSubscriptionTabView extends Composite implements UserProfileTabView<UserProfileSubscriptionPlace> {
|
||||
|
||||
public class UserProfileSubscriptionTabView extends Composite
|
||||
implements UserProfileTabView<UserProfileSubscriptionPlace> {
|
||||
|
||||
private UserProfileSubscriptionView view;
|
||||
private UserProfileSubscriptionView.Presenter currentPresenter;
|
||||
|
||||
@Override
|
||||
public Class<UserProfileSubscriptionPlace> getPlaceClassForActivation() {
|
||||
// TODO Auto-generated method stub
|
||||
return UserProfileSubscriptionPlace.class;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -8,9 +8,9 @@ import com.sap.sse.security.ui.authentication.decorator.NotLoggedInPresenter;
|
||||
public interface UserProfileSubscriptionView extends IsWidget {
|
||||
|
||||
void setPresenter(Presenter presenter);
|
||||
|
||||
|
||||
NeedsAuthenticationContext getDecorator();
|
||||
|
||||
|
||||
public interface Presenter extends NotLoggedInPresenter, NeedsAuthenticationContext {
|
||||
UserSubscriptionView.Presenter getUserSubscriptionPresenter();
|
||||
}
|
||||
|
||||
+6
-4
@@ -14,9 +14,11 @@ public class UserProfileSubscriptionViewImpl extends Composite implements UserPr
|
||||
}
|
||||
|
||||
private static MyBinder uiBinder = GWT.create(MyBinder.class);
|
||||
|
||||
@UiField(provided = true) AuthorizedContentDecoratorDesktop decoratorUi;
|
||||
@UiField(provided = true) UserSubscription userSubscriptionUi;
|
||||
|
||||
@UiField(provided = true)
|
||||
AuthorizedContentDecoratorDesktop decoratorUi;
|
||||
@UiField(provided = true)
|
||||
UserSubscription userSubscriptionUi;
|
||||
|
||||
@Override
|
||||
public void setPresenter(Presenter presenter) {
|
||||
@@ -24,7 +26,7 @@ public class UserProfileSubscriptionViewImpl extends Composite implements UserPr
|
||||
userSubscriptionUi = new UserSubscription(presenter.getUserSubscriptionPresenter());
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
|
||||
+59
-50
@@ -25,64 +25,76 @@ import com.sap.sailing.gwt.ui.shared.subscription.SubscriptionPlans;
|
||||
import com.sap.sailing.gwt.ui.shared.subscription.SubscriptionPlans.Plan;
|
||||
|
||||
/**
|
||||
* View for displaying user subscription information like plan, subscription status...
|
||||
* In this view user is able to subscribe to a plan or cancel current subscription.
|
||||
* View for displaying user subscription information like plan, subscription status...In this view user is able to
|
||||
* subscribe to a plan or cancel current subscription.
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class UserSubscription extends Composite implements UserSubscriptionView {
|
||||
interface MyUiBinder extends UiBinder<Widget, UserSubscription> {}
|
||||
interface MyUiBinder extends UiBinder<Widget, UserSubscription> {
|
||||
}
|
||||
|
||||
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
|
||||
|
||||
@UiField DivElement rootUi;
|
||||
@UiField Button updateSubscriptionButtonUi;
|
||||
@UiField Button cancelSubscriptionButtonUi;
|
||||
@UiField SpanElement planNameSpanUi;
|
||||
@UiField DivElement trialDivUi;
|
||||
@UiField DivElement subscriptionGroupUi;
|
||||
@UiField SpanElement subscriptionStatusSpanUi;
|
||||
@UiField SpanElement paymentStatusSpanUi;
|
||||
@UiField DivElement paymentStatusDivUi;
|
||||
@UiField ListBox planListUi;
|
||||
|
||||
|
||||
@UiField
|
||||
DivElement rootUi;
|
||||
@UiField
|
||||
Button updateSubscriptionButtonUi;
|
||||
@UiField
|
||||
Button cancelSubscriptionButtonUi;
|
||||
@UiField
|
||||
SpanElement planNameSpanUi;
|
||||
@UiField
|
||||
DivElement trialDivUi;
|
||||
@UiField
|
||||
DivElement subscriptionGroupUi;
|
||||
@UiField
|
||||
SpanElement subscriptionStatusSpanUi;
|
||||
@UiField
|
||||
SpanElement paymentStatusSpanUi;
|
||||
@UiField
|
||||
DivElement paymentStatusDivUi;
|
||||
@UiField
|
||||
ListBox planListUi;
|
||||
|
||||
private Presenter presenter;
|
||||
|
||||
public UserSubscription(UserSubscriptionView.Presenter presenter) {
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
|
||||
|
||||
presenter.setView(this);
|
||||
this.presenter = presenter;
|
||||
}
|
||||
|
||||
|
||||
@UiHandler("updateSubscriptionButtonUi")
|
||||
public void handleUpdateSubscriptionClick(ClickEvent e) {
|
||||
updateSubscriptionButtonUi.setEnabled(false);
|
||||
presenter.openCheckout(planListUi.getSelectedValue());
|
||||
}
|
||||
|
||||
|
||||
@UiHandler("cancelSubscriptionButtonUi")
|
||||
public void handleCancelSubscriptionClick(ClickEvent e) {
|
||||
cancelSubscriptionButtonUi.setEnabled(false);
|
||||
presenter.cancelSubscription();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onLoad() {
|
||||
super.onLoad();
|
||||
SubscriptionProfileDesktopResources.INSTANCE.css().ensureInjected();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStartLoadSubscription() {
|
||||
hide();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onOpenCheckoutError(String error) {
|
||||
updateSubscriptionButtonUi.setEnabled(true);
|
||||
Window.alert(error);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onCloseCheckoutModal() {
|
||||
updateSubscriptionButtonUi.setEnabled(true);
|
||||
@@ -91,16 +103,16 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
@Override
|
||||
public void updateView(SubscriptionDTO subscription) {
|
||||
resetElementsVisibleState();
|
||||
|
||||
|
||||
updatePlanList(subscription);
|
||||
|
||||
|
||||
updateSubscriptionButtonUi.setEnabled(true);
|
||||
|
||||
|
||||
Plan plan = null;
|
||||
if (subscription != null) {
|
||||
plan = SubscriptionPlans.getPlan(subscription.planId);
|
||||
}
|
||||
|
||||
|
||||
if (subscription == null || plan == null) {
|
||||
planNameSpanUi.setInnerText("Free");
|
||||
hideElement(subscriptionGroupUi);
|
||||
@@ -108,33 +120,32 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
show();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
planNameSpanUi.setInnerText(plan.getName());
|
||||
subscriptionStatusSpanUi.setInnerText(subscription.getSubscriptionStatusLabel());
|
||||
if (subscription.isActive()) {
|
||||
subscriptionStatusSpanUi.addClassName(SubscriptionProfileDesktopResources.INSTANCE.css().blueText());
|
||||
paymentStatusSpanUi.setInnerText(subscription.getPaymentStatusLabel());
|
||||
paymentStatusSpanUi.addClassName(
|
||||
subscription.isPaymentSuccess() ?
|
||||
SubscriptionProfileDesktopResources.INSTANCE.css().blueText() :
|
||||
SubscriptionProfileDesktopResources.INSTANCE.css().errorText());
|
||||
subscription.isPaymentSuccess() ? SubscriptionProfileDesktopResources.INSTANCE.css().blueText()
|
||||
: SubscriptionProfileDesktopResources.INSTANCE.css().errorText());
|
||||
} else {
|
||||
hideElement(paymentStatusDivUi);
|
||||
|
||||
|
||||
if (subscription.isInTrial()) {
|
||||
trialDivUi.setInnerText(buildTrialText(subscription));
|
||||
} else {
|
||||
hideElement(trialDivUi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
|
||||
private void updatePlanList(SubscriptionDTO subscription) {
|
||||
planListUi.clear();
|
||||
if (subscription == null) {
|
||||
planListUi.addItem("", "-- Select plan --");
|
||||
planListUi.addItem("", "");
|
||||
planListUi.setSelectedIndex(0);
|
||||
}
|
||||
List<Plan> planList = SubscriptionPlans.getPlanList();
|
||||
@@ -146,7 +157,7 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String buildTrialText(SubscriptionDTO subscription) {
|
||||
long now = Math.round(Duration.currentTimeMillis() / 1000);
|
||||
long remain = subscription.trialEnd - now;
|
||||
@@ -162,7 +173,7 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
}
|
||||
}
|
||||
remain = remain % 86400;
|
||||
int hours = (int)(remain / 3600);
|
||||
int hours = (int) (remain / 3600);
|
||||
if (hours > 0) {
|
||||
if (remainText.length() > 0) {
|
||||
remainText.append(" ");
|
||||
@@ -174,7 +185,7 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
}
|
||||
if (days == 0) {
|
||||
remain = remain % 3600;
|
||||
int mins = (int)(remain / 60);
|
||||
int mins = (int) (remain / 60);
|
||||
if (mins > 0) {
|
||||
if (remainText.length() > 0) {
|
||||
remainText.append(" ");
|
||||
@@ -186,40 +197,38 @@ public class UserSubscription extends Composite implements UserSubscriptionView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (remainText.length() == 0) {
|
||||
remainText.append("0 day");
|
||||
}
|
||||
|
||||
|
||||
StringBuilder trialText = new StringBuilder();
|
||||
trialText.append("Your trial expires in ")
|
||||
.append(remainText.toString())
|
||||
.append(" (").append("ends on ")
|
||||
.append(DateTimeFormat.getFormat("yyyy-MM-dd HH:mm").format(new Date(subscription.trialEnd * 1000)))
|
||||
.append(")");
|
||||
|
||||
trialText.append("Your trial expires in ").append(remainText.toString()).append(" (").append("ends on ")
|
||||
.append(DateTimeFormat.getFormat("yyyy-MM-dd HH:mm").format(new Date(subscription.trialEnd * 1000)))
|
||||
.append(")");
|
||||
|
||||
return trialText.toString();
|
||||
}
|
||||
|
||||
|
||||
private void resetElementsVisibleState() {
|
||||
showElement(subscriptionGroupUi);
|
||||
cancelSubscriptionButtonUi.setVisible(true);
|
||||
showElement(paymentStatusDivUi);
|
||||
showElement(trialDivUi);
|
||||
}
|
||||
|
||||
|
||||
private void hideElement(Element element) {
|
||||
element.getStyle().setDisplay(Display.NONE);
|
||||
}
|
||||
|
||||
|
||||
private void showElement(Element element) {
|
||||
element.getStyle().setDisplay(Display.BLOCK);
|
||||
}
|
||||
|
||||
|
||||
private void hide() {
|
||||
rootUi.getStyle().setDisplay(Display.NONE);
|
||||
}
|
||||
|
||||
|
||||
private void show() {
|
||||
rootUi.getStyle().setDisplay(Display.BLOCK);
|
||||
}
|
||||
|
||||
+63
-82
@@ -14,82 +14,73 @@ import com.sap.sse.security.ui.authentication.WithAuthenticationManager;
|
||||
import com.sap.sse.security.ui.authentication.WithUserService;
|
||||
|
||||
/**
|
||||
* Presenter for {@link UserSubscriptionView}, implementation of {@link UserSubscriptionView.Presenter}},
|
||||
* which handles initializing Chargebee, opening and executing checkout,
|
||||
* requesting user subscription data, and canceling user subscription
|
||||
* Presenter for {@link UserSubscriptionView}, implementation of {@link UserSubscriptionView.Presenter}}, which handles
|
||||
* initializing Chargebee, opening and executing checkout, requesting user subscription data, and canceling user
|
||||
* subscription
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class UserSubscriptionPresenter<C extends ClientFactoryWithDispatch & ErrorAndBusyClientFactory & WithAuthenticationManager & WithUserService & WithSubscriptionService>
|
||||
implements UserSubscriptionView.Presenter {
|
||||
|
||||
implements UserSubscriptionView.Presenter {
|
||||
|
||||
private final C clientFactory;
|
||||
private UserSubscriptionView view;
|
||||
|
||||
|
||||
/**
|
||||
* Callback for Chargebee checkout success event
|
||||
*/
|
||||
private CheckoutOption.SuccessCallback onCheckoutSuccessCallback =
|
||||
new CheckoutOption.SuccessCallback() {
|
||||
|
||||
@Override
|
||||
public void call(String hostedPageId) {
|
||||
requestFinishingPlanUpgrading(hostedPageId);
|
||||
}
|
||||
};
|
||||
|
||||
private CheckoutOption.SuccessCallback onCheckoutSuccessCallback = new CheckoutOption.SuccessCallback() {
|
||||
|
||||
@Override
|
||||
public void call(String hostedPageId) {
|
||||
requestFinishingPlanUpgrading(hostedPageId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for Chargebee checkout fail event
|
||||
*/
|
||||
private CheckoutOption.ErrorCallback onCheckoutErrorCallback =
|
||||
new CheckoutOption.ErrorCallback() {
|
||||
|
||||
@Override
|
||||
public void call(String error) {
|
||||
view.onOpenCheckoutError(error);
|
||||
}
|
||||
};
|
||||
|
||||
private CheckoutOption.ErrorCallback onCheckoutErrorCallback = new CheckoutOption.ErrorCallback() {
|
||||
|
||||
@Override
|
||||
public void call(String error) {
|
||||
view.onOpenCheckoutError(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for Chargebee checkout modal close event
|
||||
*/
|
||||
private CheckoutOption.CloseCallback onCheckoutCloseCallback =
|
||||
new CheckoutOption.CloseCallback() {
|
||||
|
||||
@Override
|
||||
public void call() {
|
||||
view.onCloseCheckoutModal();
|
||||
}
|
||||
};
|
||||
|
||||
private CheckoutOption.CloseCallback onCheckoutCloseCallback = new CheckoutOption.CloseCallback() {
|
||||
|
||||
@Override
|
||||
public void call() {
|
||||
view.onCloseCheckoutModal();
|
||||
}
|
||||
};
|
||||
|
||||
public UserSubscriptionPresenter(C clientFactory) {
|
||||
this.clientFactory = clientFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init Chargebee
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
Chargebee.init(Chargebee.InitOption.create(SubscriptionConfiguration.CHARGEBEE_SITE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load user's subscription data
|
||||
*/
|
||||
@Override
|
||||
public void loadSubscription() {
|
||||
view.onStartLoadSubscription();
|
||||
|
||||
|
||||
clientFactory.getSubscriptionService().getSubscription(new AsyncCallback<SubscriptionDTO>() {
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess(SubscriptionDTO result) {
|
||||
if (result != null && result.error != null && !result.error.isEmpty()) {
|
||||
Window.alert("Get user subscription error: " + result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
view.updateView(result);
|
||||
}
|
||||
|
||||
@@ -102,56 +93,46 @@ public class UserSubscriptionPresenter<C extends ClientFactoryWithDispatch & Err
|
||||
|
||||
@Override
|
||||
public void setView(UserSubscriptionView view) {
|
||||
this.view = view;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Chargebee checkout modal from which user can create new subscription or change one if user is already in a subscription plan
|
||||
*
|
||||
* @param planId Id of plan user want to subscribe to
|
||||
*/
|
||||
@Override
|
||||
public void openCheckout(String planId) {
|
||||
clientFactory.getSubscriptionService().generateHostedPageObject(planId, new AsyncCallback<HostedPageResultDTO>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(HostedPageResultDTO hostedPage) {
|
||||
if (hostedPage.error != null && !hostedPage.error.isEmpty()) {
|
||||
view.onOpenCheckoutError(hostedPage.error);
|
||||
} else if (hostedPage.hostedPageJSONString != null && !hostedPage.hostedPageJSONString.isEmpty()) {
|
||||
Chargebee.getInstance().openCheckout(
|
||||
CheckoutOption.create(
|
||||
hostedPage.hostedPageJSONString,
|
||||
onCheckoutSuccessCallback,
|
||||
onCheckoutErrorCallback,
|
||||
onCheckoutCloseCallback
|
||||
));;
|
||||
} else {
|
||||
view.onOpenCheckoutError("Failed to generating hosted page object, please try again");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
view.onOpenCheckoutError("Checkout error: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
clientFactory.getSubscriptionService().generateHostedPageObject(planId,
|
||||
new AsyncCallback<HostedPageResultDTO>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(HostedPageResultDTO hostedPage) {
|
||||
if (hostedPage.error != null && !hostedPage.error.isEmpty()) {
|
||||
view.onOpenCheckoutError(hostedPage.error);
|
||||
} else if (hostedPage.hostedPageJSONString != null
|
||||
&& !hostedPage.hostedPageJSONString.isEmpty()) {
|
||||
Chargebee.getInstance().openCheckout(CheckoutOption.create(hostedPage.hostedPageJSONString,
|
||||
onCheckoutSuccessCallback, onCheckoutErrorCallback, onCheckoutCloseCallback));
|
||||
;
|
||||
} else {
|
||||
view.onOpenCheckoutError("Failed to generating hosted page object, please try again");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
view.onOpenCheckoutError("Checkout error: " + caught.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel current user's subscription
|
||||
*/
|
||||
|
||||
@Override
|
||||
public void cancelSubscription() {
|
||||
clientFactory.getSubscriptionService().cancelSubscription(new AsyncCallback<Boolean>() {
|
||||
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
if (!result) {
|
||||
Window.alert("Failed to cancel the subscription");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
view.updateView(null);
|
||||
}
|
||||
|
||||
@@ -161,7 +142,7 @@ public class UserSubscriptionPresenter<C extends ClientFactoryWithDispatch & Err
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void requestFinishingPlanUpgrading(String hostedPageId) {
|
||||
clientFactory.getSubscriptionService().updatePlanSuccess(hostedPageId, new AsyncCallback<SubscriptionDTO>() {
|
||||
|
||||
@@ -170,13 +151,13 @@ public class UserSubscriptionPresenter<C extends ClientFactoryWithDispatch & Err
|
||||
view.updateView(result);
|
||||
Chargebee.getInstance().closeAll();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
Chargebee.getInstance().closeAll();
|
||||
Window.alert("Saving subscription data error: " + caught.getMessage());
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+45
-1
@@ -4,16 +4,60 @@ import com.google.gwt.user.client.ui.IsWidget;
|
||||
import com.sap.sailing.gwt.ui.shared.subscription.SubscriptionDTO;
|
||||
|
||||
public interface UserSubscriptionView extends IsWidget {
|
||||
/**
|
||||
* This is called on start loading subscription data
|
||||
*/
|
||||
public void onStartLoadSubscription();
|
||||
|
||||
/**
|
||||
* Call to update the view with subscription data returned from backend
|
||||
*
|
||||
* @param subscription
|
||||
*/
|
||||
public void updateView(SubscriptionDTO subscription);
|
||||
|
||||
/**
|
||||
* Called on Chargebee checkout modal is closed
|
||||
*/
|
||||
public void onCloseCheckoutModal();
|
||||
|
||||
/**
|
||||
* Called on openning Chargebee checkout modal has errors
|
||||
*
|
||||
* @param error
|
||||
*/
|
||||
public void onOpenCheckoutError(String error);
|
||||
|
||||
|
||||
/**
|
||||
* Presenter for {@link UserSubscriptionView}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public interface Presenter {
|
||||
/**
|
||||
* Init Chargebee
|
||||
*/
|
||||
public void init();
|
||||
|
||||
/**
|
||||
* Load user's subscription data
|
||||
*/
|
||||
public void loadSubscription();
|
||||
|
||||
/**
|
||||
* Open Chargebee checkout modal from which user can create new subscription or change one if user is already in
|
||||
* a subscription plan
|
||||
*
|
||||
* @param planId
|
||||
* Id of plan user want to subscribe to
|
||||
*/
|
||||
public void openCheckout(String planId);
|
||||
|
||||
public void setView(UserSubscriptionView view);
|
||||
|
||||
/**
|
||||
* Request to cancel current user's subscription
|
||||
*/
|
||||
public void cancelSubscription();
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -14,24 +14,27 @@ import jsinterop.annotations.JsType;
|
||||
public class Chargebee {
|
||||
/**
|
||||
* Init Chargebee module {@link https://www.chargebee.com/checkout-portal-docs/api.html#chargebee-object}
|
||||
*
|
||||
* @param options
|
||||
* @return
|
||||
*/
|
||||
public static native ChargebeeInstance init(InitOption options);
|
||||
|
||||
|
||||
/**
|
||||
* Get Chargebee instance which is available only after initialization
|
||||
* {@link https://www.chargebee.com/checkout-portal-docs/api.html#getinstance}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static native ChargebeeInstance getInstance();
|
||||
|
||||
|
||||
public static class InitOption extends JavaScriptObject {
|
||||
protected InitOption() {}
|
||||
|
||||
protected InitOption() {
|
||||
}
|
||||
|
||||
public static native InitOption create(String site) /*-{
|
||||
return {
|
||||
site: site
|
||||
site : site
|
||||
};
|
||||
}-*/;
|
||||
}
|
||||
|
||||
+11
-6
@@ -11,24 +11,29 @@ import jsinterop.annotations.JsType;
|
||||
public class ChargebeeInstance {
|
||||
/**
|
||||
* Open Chargebee checkout modal {@link https://www.chargebee.com/checkout-portal-docs/api.html#opencheckout}
|
||||
*
|
||||
* @param option
|
||||
*/
|
||||
public native void openCheckout(CheckoutOption option);
|
||||
|
||||
|
||||
/**
|
||||
* Close Chargebee checkout modal {@link https://www.chargebee.com/checkout-portal-docs/api.html#closeall}
|
||||
*/
|
||||
public native void closeAll();
|
||||
|
||||
|
||||
/**
|
||||
* Set Chargebee customer portal session {@link https://www.chargebee.com/checkout-portal-docs/api.html#setportalsession}
|
||||
* Use this in case we want integrate customer portal in the application
|
||||
* Set Chargebee customer portal session
|
||||
* {@link https://www.chargebee.com/checkout-portal-docs/api.html#setportalsession} Use this in case we want
|
||||
* integrate customer portal in the application
|
||||
*
|
||||
* @param sessionSetter
|
||||
*/
|
||||
public native void setPortalSession(PortalSessionSetterCallback sessionSetter);
|
||||
|
||||
|
||||
/**
|
||||
* Create Chargebee customer portal instance {@link https://www.chargebee.com/checkout-portal-docs/api.html#createchargebeeportal}
|
||||
* Create Chargebee customer portal instance
|
||||
* {@link https://www.chargebee.com/checkout-portal-docs/api.html#createchargebeeportal}
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public native ChargebeePortal createChargebeePortal();
|
||||
|
||||
+13
-12
@@ -3,39 +3,40 @@ package com.sap.sailing.gwt.ui.client.subscription;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
|
||||
/**
|
||||
* Class represent JS option object for Chargebee instance openCheckout method
|
||||
* {@link ChargebeeInstance}
|
||||
* Class represent JS option object for Chargebee instance openCheckout method {@link ChargebeeInstance}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class CheckoutOption extends JavaScriptObject {
|
||||
protected CheckoutOption() {}
|
||||
|
||||
public static native CheckoutOption create(String hostedPage, SuccessCallback onSuccess, ErrorCallback onError, CloseCallback onClose) /*-{
|
||||
protected CheckoutOption() {
|
||||
}
|
||||
|
||||
public static native CheckoutOption create(String hostedPage, SuccessCallback onSuccess, ErrorCallback onError,
|
||||
CloseCallback onClose) /*-{
|
||||
return {
|
||||
hostedPage: function() {
|
||||
hostedPage : function() {
|
||||
return Promise.resolve(JSON.parse(hostedPage));
|
||||
},
|
||||
success: function(hostedPageId) {
|
||||
success : function(hostedPageId) {
|
||||
onSuccess.@com.sap.sailing.gwt.ui.client.subscription.CheckoutOption.SuccessCallback::call(Ljava/lang/String;)(hostedPageId);
|
||||
},
|
||||
error: function(error) {
|
||||
error : function(error) {
|
||||
onError.@com.sap.sailing.gwt.ui.client.subscription.CheckoutOption.ErrorCallback::call(Ljava/lang/String;)(error.message ? error.message : error);
|
||||
},
|
||||
close: function() {
|
||||
close : function() {
|
||||
onClose.@com.sap.sailing.gwt.ui.client.subscription.CheckoutOption.CloseCallback::call()();
|
||||
}
|
||||
};
|
||||
}-*/;
|
||||
|
||||
|
||||
public static interface SuccessCallback {
|
||||
void call(String hostedPageId);
|
||||
}
|
||||
|
||||
|
||||
public static interface ErrorCallback {
|
||||
void call(String error);
|
||||
}
|
||||
|
||||
|
||||
public static abstract interface CloseCallback {
|
||||
void call();
|
||||
}
|
||||
|
||||
+6
-6
@@ -3,22 +3,22 @@ package com.sap.sailing.gwt.ui.client.subscription;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
|
||||
/**
|
||||
* Class represent JS option object for Chargebee portal instance open method
|
||||
* {@link ChargebeePortal}
|
||||
* Class represent JS option object for Chargebee portal instance open method {@link ChargebeePortal}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class PortalOption extends JavaScriptObject {
|
||||
protected PortalOption() {}
|
||||
|
||||
protected PortalOption() {
|
||||
}
|
||||
|
||||
public static native PortalOption create(CloseCallback onClose) /*-{
|
||||
return {
|
||||
close: function() {
|
||||
close : function() {
|
||||
onClose.@com.sap.sailing.gwt.ui.client.subscription.PortalOption.CloseCallback::call()();
|
||||
}
|
||||
};
|
||||
}-*/;
|
||||
|
||||
|
||||
public static interface CloseCallback {
|
||||
void call();
|
||||
}
|
||||
|
||||
+5
-5
@@ -3,18 +3,18 @@ package com.sap.sailing.gwt.ui.client.subscription;
|
||||
import com.google.gwt.core.client.JavaScriptObject;
|
||||
|
||||
/**
|
||||
* Class represent Chargeebee portal session callback function
|
||||
* {@link ChargebeeInstance}
|
||||
* Class represent Chargeebee portal session callback function {@link ChargebeeInstance#setPortalSession(PortalSessionSetterCallback)}
|
||||
* {@link https://www.chargebee.com/checkout-portal-docs/api.html#setportalsession}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class PortalSessionSetterCallback extends JavaScriptObject {
|
||||
protected PortalSessionSetterCallback() {}
|
||||
|
||||
protected PortalSessionSetterCallback() {
|
||||
}
|
||||
|
||||
public static native PortalSessionSetterCallback create(String portalSession) /*-{
|
||||
return function() {
|
||||
return new Promise(function (resolve) {
|
||||
return new Promise(function(resolve) {
|
||||
resolve(JSON.parse(portalSession));
|
||||
});
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
package com.sap.sailing.gwt.ui.client.subscription;
|
||||
|
||||
/**
|
||||
* Chargebee configuration.
|
||||
* From frontend side we need to know Chargebee site
|
||||
* Chargebee configuration. From frontend side we need to know Chargebee site
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
|
||||
+25
@@ -10,8 +10,33 @@ import com.sap.sailing.gwt.ui.shared.subscription.SubscriptionDTO;
|
||||
* @author tutran
|
||||
*/
|
||||
public interface SubscriptionService extends RemoteService {
|
||||
/**
|
||||
* Generate Chargebee checkout hosted page object. Client has to call this method to get the hosted page before
|
||||
* opening Chargebee checkout modal Check flow of getting hosted page object from here
|
||||
* {@link https://www.chargebee.com/checkout-portal-docs/api-checkout.html#call-flow}
|
||||
*
|
||||
* @param planId
|
||||
* Plan id to subscribe to
|
||||
*/
|
||||
public HostedPageResultDTO generateHostedPageObject(String planId);
|
||||
|
||||
/**
|
||||
* Call this method from frontend after checkout is success. This method will send acknowledge request to Chargebee
|
||||
* for the checkout, and update user's subscription, and send back subscription information to frontend for updating
|
||||
* view.
|
||||
*
|
||||
* @param hostedPageId
|
||||
* the success hosted page id which is returned from Chargebee
|
||||
*/
|
||||
public SubscriptionDTO updatePlanSuccess(String hostedPageId);
|
||||
|
||||
/**
|
||||
* Fetch user current subscription data from database
|
||||
*/
|
||||
public SubscriptionDTO getSubscription();
|
||||
|
||||
/**
|
||||
* Cancel current user subscription
|
||||
*/
|
||||
public boolean cancelSubscription();
|
||||
}
|
||||
|
||||
+8
@@ -4,9 +4,17 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.gwt.ui.shared.subscription.HostedPageResultDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.subscription.SubscriptionDTO;
|
||||
|
||||
/**
|
||||
* Async remote service interface for {@link SubscriptionService}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public interface SubscriptionServiceAsync {
|
||||
public void generateHostedPageObject(String planId, AsyncCallback<HostedPageResultDTO> callback);
|
||||
|
||||
public void updatePlanSuccess(String hostedPageId, AsyncCallback<SubscriptionDTO> callback);
|
||||
|
||||
public void getSubscription(AsyncCallback<SubscriptionDTO> callback);
|
||||
|
||||
void cancelSubscription(AsyncCallback<Boolean> callback);
|
||||
}
|
||||
|
||||
+12
-12
@@ -4,43 +4,43 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Chargebee subscription configuration
|
||||
* Chargebee subscription configuration We need the site and api key information. The system will get these information
|
||||
* from server application startup arguments: chargebee.site, chargebee.apikey
|
||||
*/
|
||||
public class SubscriptionConfiguration {
|
||||
private static final Logger logger = Logger.getLogger(SubscriptionConfiguration.class.getName());
|
||||
|
||||
|
||||
private static final String CHARGEBEE_SITE = "chargebee.site";
|
||||
private static final String CHARGEBEE_APIKEY = "chargebee.apikey";
|
||||
|
||||
|
||||
private static SubscriptionConfiguration instance;
|
||||
|
||||
|
||||
private String site;
|
||||
private String apiKey;
|
||||
|
||||
|
||||
protected SubscriptionConfiguration(String site, String apiKey) {
|
||||
this.site = site;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
|
||||
public static SubscriptionConfiguration getInstance() {
|
||||
if (instance == null) {
|
||||
String site = System.getProperty(CHARGEBEE_SITE);
|
||||
String apiKey = System.getProperty(CHARGEBEE_APIKEY);
|
||||
|
||||
|
||||
logger.log(Level.INFO, "Chargebee site: " + site + ", apiKey: " + apiKey);
|
||||
|
||||
|
||||
instance = new SubscriptionConfiguration(site, apiKey);
|
||||
}
|
||||
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public String getSite() {
|
||||
return site;
|
||||
}
|
||||
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
|
||||
+75
-73
@@ -26,21 +26,28 @@ import com.sap.sse.security.shared.Subscription;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.ui.server.Activator;
|
||||
|
||||
/**
|
||||
* Backend implementation of {@link SubscriptionService} remote service interface.
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class SubscriptionServiceImpl extends RemoteServiceServlet implements SubscriptionService {
|
||||
private static final long serialVersionUID = -4276839013785711262L;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SubscriptionServiceImpl.class.getName());
|
||||
|
||||
|
||||
private final BundleContext context;
|
||||
private final FutureTask<SecurityService> securityService;
|
||||
|
||||
|
||||
public SubscriptionServiceImpl() {
|
||||
Environment.configure(
|
||||
SubscriptionConfiguration.getInstance().getSite(),
|
||||
// Configure Chargebee
|
||||
Environment.configure(SubscriptionConfiguration.getInstance().getSite(),
|
||||
SubscriptionConfiguration.getInstance().getApiKey());
|
||||
|
||||
|
||||
// get SecurityService
|
||||
context = Activator.getContext();
|
||||
final ServiceTracker<SecurityService, SecurityService> tracker = new ServiceTracker<>(context, SecurityService.class, /* customizer */ null);
|
||||
final ServiceTracker<SecurityService, SecurityService> tracker = new ServiceTracker<>(context,
|
||||
SecurityService.class, /* customizer */ null);
|
||||
tracker.open();
|
||||
securityService = new FutureTask<SecurityService>(new Callable<SecurityService>() {
|
||||
@Override
|
||||
@@ -49,7 +56,7 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
try {
|
||||
logger.info("Waiting for SecurityService...");
|
||||
result = tracker.waitForService(0);
|
||||
logger.info("Obtained SecurityService "+result);
|
||||
logger.info("Obtained SecurityService " + result);
|
||||
return result;
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.SEVERE, "Interrupted while waiting for UserStore service", e);
|
||||
@@ -57,7 +64,7 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
return result;
|
||||
}
|
||||
});
|
||||
new Thread("ServiceTracker in bundle com.sap.sailing.gwt.ui.server for SecurityService") {
|
||||
new Thread("ServiceTracker in bundle com.sap.sailing.gwt.ui.server.subscription for SecurityService") {
|
||||
@Override
|
||||
public void run() {
|
||||
securityService.run();
|
||||
@@ -68,22 +75,30 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
|
||||
@Override
|
||||
public HostedPageResultDTO generateHostedPageObject(String planId) {
|
||||
|
||||
HostedPageResultDTO response = new HostedPageResultDTO();
|
||||
|
||||
// Validate if plan id is valid
|
||||
if (planId == null || planId.isEmpty() || SubscriptionPlans.getPlan(planId) == null) {
|
||||
response.error = "Invalid plan";
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
User user = getCurrentUser();
|
||||
|
||||
if (user.getSubscription() != null && user.getSubscription().planId != null && user.getSubscription().planId.equals(planId)) {
|
||||
response.error = "User has already subscribed to " + SubscriptionPlans.getPlan(planId).getName() + " plan";
|
||||
|
||||
// Check if user already subscribed to a plan, and if it's same plan with new plan then we stop the process
|
||||
// and send back error
|
||||
if (user.getSubscription() != null && user.getSubscription().planId != null
|
||||
&& user.getSubscription().planId.equals(planId)) {
|
||||
response.error = "User has already subscribed to " + SubscriptionPlans.getPlan(planId).getName()
|
||||
+ " plan";
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Result result;
|
||||
|
||||
|
||||
// If there's no subscription data attach to user model then we create a checkout-new request to Chargebee
|
||||
if (user.getSubscription() == null || user.getSubscription().planId == null) {
|
||||
String[] userNameParts = user.getFullName().split("\\s+");
|
||||
String firstName = userNameParts[0];
|
||||
@@ -91,45 +106,43 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
if (userNameParts.length > 1) {
|
||||
lastName = String.join(" ", Arrays.copyOfRange(userNameParts, 1, userNameParts.length));
|
||||
}
|
||||
|
||||
|
||||
String locale = user.getLocaleOrDefault().getLanguage();
|
||||
|
||||
|
||||
// Send checkout-new request to Chargebee with all necessary customer information and get back hosted
|
||||
// page result object
|
||||
result = HostedPage.checkoutNew()
|
||||
.customerId(user.getName())
|
||||
.customerEmail(user.getEmail())
|
||||
.customerFirstName(firstName)
|
||||
.customerLastName(lastName)
|
||||
.customerLocale(locale)
|
||||
.subscriptionPlanId(planId)
|
||||
.billingAddressFirstName(firstName)
|
||||
.billingAddressLastName(lastName)
|
||||
.billingAddressCountry("US").request();
|
||||
} else {
|
||||
result = HostedPage.checkoutExisting()
|
||||
.subscriptionId(user.getSubscription().subscriptionId)
|
||||
.subscriptionPlanId(planId)
|
||||
// here Chargebee's customer id is same as the system user name
|
||||
.customerId(user.getName()).customerEmail(user.getEmail()).customerFirstName(firstName)
|
||||
.customerLastName(lastName).customerLocale(locale).subscriptionPlanId(planId)
|
||||
.billingAddressFirstName(firstName).billingAddressLastName(lastName).billingAddressCountry("US")
|
||||
.request();
|
||||
} else {
|
||||
// User has already subscribed to a plan, so here user wants to change plan
|
||||
// and we make a checkout-existing request to Chargebee, and get back hosted page object
|
||||
result = HostedPage.checkoutExisting().subscriptionId(user.getSubscription().subscriptionId)
|
||||
.subscriptionPlanId(planId).request();
|
||||
}
|
||||
|
||||
|
||||
response.hostedPageJSONString = result.hostedPage().toJson();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
|
||||
logger.log(Level.WARNING, "Error in generating Chargebee hosted page data ", e);
|
||||
|
||||
|
||||
response.error = "Error in generating Chargebee hosted page";
|
||||
}
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SubscriptionDTO updatePlanSuccess(String hostedPageId) {
|
||||
SubscriptionDTO subscriptionDto = new SubscriptionDTO();
|
||||
|
||||
|
||||
try {
|
||||
User user = getCurrentUser();
|
||||
|
||||
|
||||
Result result = HostedPage.acknowledge(hostedPageId).request();
|
||||
Content content = result.hostedPage().content();
|
||||
Subscription subscription = new Subscription();
|
||||
@@ -143,36 +156,35 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
subscription.subsciptionUpdatedAt = Math.round(content.subscription().updatedAt().getTime() / 1000);
|
||||
subscription.latestEventTime = 0;
|
||||
subscription.manualUpdatedAt = Math.round(System.currentTimeMillis() / 1000);
|
||||
|
||||
|
||||
getSecurityService().updateUserSubscription(user.getName(), subscription);
|
||||
|
||||
|
||||
subscriptionDto.planId = subscription.planId;
|
||||
subscriptionDto.trialStart = subscription.trialStart;
|
||||
subscriptionDto.trialEnd = subscription.trialEnd;
|
||||
subscriptionDto.subscriptionStatus = subscription.subscriptionStatus;
|
||||
subscriptionDto.paymentStatus = subscription.paymentStatus;
|
||||
|
||||
return subscriptionDto;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.log(Level.WARNING, "Error in saving subscription ", e);
|
||||
|
||||
|
||||
subscriptionDto.error = e.getMessage();
|
||||
return subscriptionDto;
|
||||
}
|
||||
|
||||
return subscriptionDto;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SubscriptionDTO getSubscription() {
|
||||
SubscriptionDTO subscriptionDto = new SubscriptionDTO();
|
||||
try {
|
||||
User user = getCurrentUser();
|
||||
|
||||
|
||||
Subscription subscription = user.getSubscription();
|
||||
if (subscription == null || subscription.planId == null || subscription.planId.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
subscriptionDto.planId = subscription.planId;
|
||||
subscriptionDto.subscriptionStatus = subscription.subscriptionStatus;
|
||||
subscriptionDto.paymentStatus = subscription.paymentStatus;
|
||||
@@ -181,13 +193,13 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.log(Level.WARNING, "Error in getting subscription ", e);
|
||||
|
||||
|
||||
subscriptionDto.error = e.getMessage();
|
||||
}
|
||||
|
||||
|
||||
return subscriptionDto;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean cancelSubscription() {
|
||||
try {
|
||||
@@ -196,16 +208,20 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
if (subscription == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
String subscriptionId = subscription.subscriptionId;
|
||||
if (subscriptionId != null && !subscriptionId.isEmpty()) {
|
||||
Result result = com.chargebee.models.Subscription.cancel(subscriptionId)
|
||||
.request();
|
||||
if (!result.subscription().status().name().toLowerCase().equals(Subscription.SUBSCRIPTION_STATUS_CANCELLED)) {
|
||||
// Send cancel request to Chargebee and verify result and only process if the result's subscription
|
||||
// status is updated to be cancelled
|
||||
Result result = com.chargebee.models.Subscription.cancel(subscriptionId).request();
|
||||
if (!result.subscription().status().name().toLowerCase()
|
||||
.equals(Subscription.SUBSCRIPTION_STATUS_CANCELLED)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// for a cancelled subscription we'll update user's subscription data with plan, subscription, status to be
|
||||
// null
|
||||
Subscription newSubscription = new Subscription();
|
||||
newSubscription.latestEventTime = subscription.latestEventTime;
|
||||
newSubscription.manualUpdatedAt = Math.round(System.currentTimeMillis() / 1000);
|
||||
@@ -218,20 +234,6 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public String generatePortalPageObject() {
|
||||
// try {
|
||||
// User user = getCurrentUser();
|
||||
// Result result = PortalSession.create().customerId(user.getName()).request();
|
||||
// PortalSession portalSession = result.portalSession();
|
||||
// return portalSession.toJson();
|
||||
// } catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
// logger.log(Level.WARNING, "Error in generating portal session page object ", e);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
private SecurityService getSecurityService() {
|
||||
try {
|
||||
return securityService.get();
|
||||
@@ -239,23 +241,23 @@ public class SubscriptionServiceImpl extends RemoteServiceServlet implements Sub
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private User getCurrentUser() throws SubscriptionException {
|
||||
User user = getSecurityService().getCurrentUser();
|
||||
if (user == null) {
|
||||
throw new SubscriptionException(SubscriptionException.INVALID_CURRENT_USER);
|
||||
}
|
||||
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
private class SubscriptionException extends Exception implements Serializable {
|
||||
private static final long serialVersionUID = 6321960099419330110L;
|
||||
|
||||
|
||||
public static final String INVALID_CURRENT_USER = "Current user not found";
|
||||
|
||||
|
||||
private final String message;
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ public class HostedPageResultDTO implements IsSerializable {
|
||||
* In success case, hostedPageJSONString has value of JSON encoded string of hosted page object
|
||||
*/
|
||||
public String hostedPageJSONString;
|
||||
|
||||
|
||||
/**
|
||||
* In fail case, error contains error message
|
||||
*/
|
||||
|
||||
+57
-8
@@ -2,32 +2,76 @@ package com.sap.sailing.gwt.ui.shared.subscription;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.subscription.SubscriptionService;
|
||||
|
||||
/**
|
||||
* User subscription data transfer object {@link SubscriptionService}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class SubscriptionDTO implements IsSerializable {
|
||||
public static String PAYMENT_STATUS_SUCCESS = "success";
|
||||
public static String PAYMENT_STATUS_NO_SUCCESS = "no_success";
|
||||
public static String SUBSCRIPTION_STATUS_TRIAL = "in_trial";
|
||||
public static String SUBSCRIPTION_STATUS_ACTIVE = "active";
|
||||
|
||||
|
||||
/**
|
||||
* User current subscription plan id
|
||||
*/
|
||||
public String planId;
|
||||
|
||||
/**
|
||||
* Trial start timestamp
|
||||
*/
|
||||
public long trialStart;
|
||||
|
||||
/**
|
||||
* Trial end timestamp
|
||||
*/
|
||||
public long trialEnd;
|
||||
|
||||
/**
|
||||
* Subscription status: active or in_trial
|
||||
*/
|
||||
public String subscriptionStatus;
|
||||
|
||||
/**
|
||||
* Subscription payment status: success or no_success
|
||||
*/
|
||||
public String paymentStatus;
|
||||
|
||||
/**
|
||||
* Error message
|
||||
*/
|
||||
public String error;
|
||||
|
||||
|
||||
/**
|
||||
* Check if subscription is in trial status
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isInTrial() {
|
||||
return subscriptionStatus.equals(SUBSCRIPTION_STATUS_TRIAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if subscription is in active status
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isActive() {
|
||||
return subscriptionStatus.equals(SUBSCRIPTION_STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
|
||||
public boolean isPaymentSuccess() {
|
||||
return paymentStatus.equals(PAYMENT_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get subscription status i18n label
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getSubscriptionStatusLabel() {
|
||||
if (subscriptionStatus != null) {
|
||||
if (subscriptionStatus.equals(SUBSCRIPTION_STATUS_TRIAL)) {
|
||||
@@ -36,10 +80,15 @@ public class SubscriptionDTO implements IsSerializable {
|
||||
return StringMessages.INSTANCE.active();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get subscription payment status i18n label
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPaymentStatusLabel() {
|
||||
if (paymentStatus != null) {
|
||||
if (paymentStatus.equals(PAYMENT_STATUS_SUCCESS)) {
|
||||
@@ -48,7 +97,7 @@ public class SubscriptionDTO implements IsSerializable {
|
||||
return StringMessages.INSTANCE.paymentStatusNoSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
+12
-7
@@ -5,37 +5,42 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Class represents all subscription plans
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class SubscriptionPlans {
|
||||
public static Plan STARTER = new Plan("starter", "Starter");
|
||||
public static Plan PREMIUM = new Plan("premium", "Premium");
|
||||
|
||||
|
||||
private static Map<String, Plan> planMap;
|
||||
private static List<Plan> planList;
|
||||
|
||||
|
||||
public static Plan getPlan(String planId) {
|
||||
if (planMap == null) {
|
||||
planMap = new HashMap<String, SubscriptionPlans.Plan>();
|
||||
planMap.put(STARTER.id, STARTER);
|
||||
planMap.put(PREMIUM.id, PREMIUM);
|
||||
}
|
||||
|
||||
|
||||
return planMap.get(planId);
|
||||
}
|
||||
|
||||
|
||||
public static List<Plan> getPlanList() {
|
||||
if (planList == null) {
|
||||
planList = new ArrayList<SubscriptionPlans.Plan>();
|
||||
planList.add(STARTER);
|
||||
planList.add(PREMIUM);
|
||||
}
|
||||
|
||||
|
||||
return planList;
|
||||
}
|
||||
|
||||
|
||||
public static class Plan {
|
||||
private String name;
|
||||
private String id;
|
||||
|
||||
|
||||
public Plan(String id, String name) {
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
|
||||
+39
-34
@@ -2,6 +2,11 @@ package com.sap.sailing.server.gateway.subscription;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
/**
|
||||
* Wrapped class for Chargebee webhook event JSON object
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class SubscriptionWebhookEvent {
|
||||
public static final String EVENT_CUSTOMER_DELETED = "customer_deleted";
|
||||
public static final String EVENT_SUBSCRIPTION_DELETED = "subscription_deleted";
|
||||
@@ -11,25 +16,25 @@ public class SubscriptionWebhookEvent {
|
||||
public static final String EVENT_PAYMENT_SUCCEEDED = "payment_succeeded";
|
||||
public static final String EVENT_PAYMENT_FAILED = "payment_failed";
|
||||
public static final String EVENT_SUBSCRIPTION_ACTIVATED = "subscription_activated";
|
||||
|
||||
|
||||
public static final String SUBSCRIPTION_STATUS_ACTIVE = "active";
|
||||
public static final String INVOICE_STATUS_PAID = "paid";
|
||||
public static final String TRANSACTION_STATUS_SUCCESS = "success";
|
||||
public static final String TRANSACTION_TYPE_PAYMENT = "payment";
|
||||
|
||||
|
||||
private JSONObject eventJSON;
|
||||
private String eventId;
|
||||
private String eventType;
|
||||
private JSONObject content;
|
||||
|
||||
|
||||
public SubscriptionWebhookEvent(JSONObject eventJSON) {
|
||||
this.eventJSON = eventJSON;
|
||||
|
||||
|
||||
eventId = getJSONString(eventJSON, "id");
|
||||
eventType = toLowerCase(getJSONString(eventJSON, "event_type"));
|
||||
content = getJSONObject(eventJSON, "content");
|
||||
}
|
||||
|
||||
|
||||
public boolean isValidEvent() {
|
||||
return eventId != null && eventType != null;
|
||||
}
|
||||
@@ -41,72 +46,72 @@ public class SubscriptionWebhookEvent {
|
||||
public String getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
|
||||
public String getCustomerEmail() {
|
||||
return getNestedJSONString(content, "customer", "email");
|
||||
}
|
||||
|
||||
|
||||
public String getCustomerId() {
|
||||
return getNestedJSONString(content, "customer", "id");
|
||||
}
|
||||
|
||||
|
||||
public String getPlanId() {
|
||||
return getNestedJSONString(content, "subscription", "plan_id");
|
||||
}
|
||||
|
||||
|
||||
public String getSubscriptionId() {
|
||||
return getNestedJSONString(content, "subscription", "id");
|
||||
}
|
||||
|
||||
|
||||
public String getSubscriptionStatus() {
|
||||
return toLowerCase(getNestedJSONString(content, "subscription", "status"));
|
||||
}
|
||||
|
||||
|
||||
public long getSubscriptionTrialStart() {
|
||||
return getNestedJSONLong(content, "subscription", "trial_start");
|
||||
}
|
||||
|
||||
|
||||
public long getSubscriptionTrialEnd() {
|
||||
return getNestedJSONLong(content, "subscription", "trial_end");
|
||||
}
|
||||
|
||||
|
||||
public long getSubscriptionCreatedAt() {
|
||||
return getNestedJSONLong(content, "subscription", "created_at");
|
||||
}
|
||||
|
||||
|
||||
public long getSubscriptionUpdatedAt() {
|
||||
return getNestedJSONLong(content, "subscription", "updated_at");
|
||||
}
|
||||
|
||||
|
||||
public long getEventOccurredAt() {
|
||||
return getJSONLong(eventJSON, "occurred_at");
|
||||
}
|
||||
|
||||
|
||||
public String getTransactionStatus() {
|
||||
return toLowerCase(getNestedJSONString(content, "transaction", "status"));
|
||||
}
|
||||
|
||||
|
||||
public String getInvoiceStatus() {
|
||||
return toLowerCase(getNestedJSONString(content, "invoice", "status"));
|
||||
}
|
||||
|
||||
|
||||
public String getTransactionType() {
|
||||
return toLowerCase(getNestedJSONString(content, "transaction", "type"));
|
||||
}
|
||||
|
||||
|
||||
private JSONObject getJSONObject(JSONObject object, String key) {
|
||||
return (JSONObject)object.get(key);
|
||||
return (JSONObject) object.get(key);
|
||||
}
|
||||
|
||||
|
||||
private String getJSONString(JSONObject object, String key) {
|
||||
return (String)object.get(key);
|
||||
return (String) object.get(key);
|
||||
}
|
||||
|
||||
|
||||
private long getJSONLong(JSONObject object, String key) {
|
||||
return (Long)object.get(key);
|
||||
return (Long) object.get(key);
|
||||
}
|
||||
|
||||
private Object getNestedJSONValue(JSONObject object, String ...keys) {
|
||||
|
||||
private Object getNestedJSONValue(JSONObject object, String... keys) {
|
||||
JSONObject tmp = object;
|
||||
Object val = null;
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
@@ -119,18 +124,18 @@ public class SubscriptionWebhookEvent {
|
||||
val = tmp.get(keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private String getNestedJSONString(JSONObject object, String ...keys) {
|
||||
return (String)getNestedJSONValue(object, keys);
|
||||
|
||||
private String getNestedJSONString(JSONObject object, String... keys) {
|
||||
return (String) getNestedJSONValue(object, keys);
|
||||
}
|
||||
|
||||
private long getNestedJSONLong(JSONObject object, String ...keys) {
|
||||
return (Long)getNestedJSONValue(object, keys);
|
||||
|
||||
private long getNestedJSONLong(JSONObject object, String... keys) {
|
||||
return (Long) getNestedJSONValue(object, keys);
|
||||
}
|
||||
|
||||
|
||||
private String toLowerCase(String str) {
|
||||
return str != null ? str.toLowerCase() : null;
|
||||
}
|
||||
|
||||
+47
-35
@@ -19,54 +19,64 @@ import com.sap.sse.security.shared.Subscription;
|
||||
import com.sap.sse.security.shared.UserManagementException;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
|
||||
/**
|
||||
* Servlet for handling Chargebee webhook events Response with status 200 in success handling
|
||||
*
|
||||
* {@link https://www.chargebee.com/docs/events_and_webhooks.html}
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class SubscriptionWebhookServlet extends SailingServerHttpServlet {
|
||||
private static final long serialVersionUID = 2608645647937414012L;
|
||||
private static final Logger logger = Logger.getLogger(SubscriptionWebhookServlet.class.getName());
|
||||
|
||||
|
||||
private static String basicAuthHeaderValue;
|
||||
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response)
|
||||
throws ServletException, IOException {
|
||||
if (!verifyBasicAuth(request)) {
|
||||
logger.log(Level.WARNING, "Invalid webhook http basic auth");
|
||||
response.setStatus(403);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Object requestBody = JSONValue.parseWithException(request.getReader());
|
||||
JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
|
||||
logger.log(Level.INFO, "Chargebee webhook data: " + requestObject.toJSONString());
|
||||
|
||||
|
||||
SubscriptionWebhookEvent event = new SubscriptionWebhookEvent(requestObject);
|
||||
if (!event.isValidEvent()) {
|
||||
throw new Exception("Invalid webhook event");
|
||||
}
|
||||
|
||||
|
||||
User user = getUser(event.getCustomerId());
|
||||
if (user == null) {
|
||||
response.setStatus(200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Subscription userSubscription = user.getSubscription();
|
||||
|
||||
|
||||
long occuredAt = event.getEventOccurredAt();
|
||||
|
||||
if (userSubscription != null &&
|
||||
occuredAt < user.getSubscription().latestEventTime &&
|
||||
occuredAt < user.getSubscription().manualUpdatedAt) {
|
||||
|
||||
// Verify the event time, only process if event time is larger than last success processed event and last
|
||||
// updated time by user
|
||||
if (userSubscription != null && occuredAt < user.getSubscription().latestEventTime
|
||||
&& occuredAt < user.getSubscription().manualUpdatedAt) {
|
||||
response.setStatus(200);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch (event.getEventType()) {
|
||||
case SubscriptionWebhookEvent.EVENT_CUSTOMER_DELETED:
|
||||
updateUserSubscription(user, buildEmptySubscription(userSubscription, event));
|
||||
break;
|
||||
case SubscriptionWebhookEvent.EVENT_SUBSCRIPTION_CANCELLED:
|
||||
case SubscriptionWebhookEvent.EVENT_SUBSCRIPTION_DELETED:
|
||||
if (userSubscription != null && userSubscription.subscriptionId !=null && userSubscription.subscriptionId.equals(event.getSubscriptionId())) {
|
||||
if (userSubscription != null && userSubscription.subscriptionId != null
|
||||
&& userSubscription.subscriptionId.equals(event.getSubscriptionId())) {
|
||||
updateUserSubscription(user, buildEmptySubscription(userSubscription, event));
|
||||
}
|
||||
break;
|
||||
@@ -78,7 +88,7 @@ public class SubscriptionWebhookServlet extends SailingServerHttpServlet {
|
||||
updateUserSubscription(user, buildSubscription(userSubscription, event));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
response.setStatus(200);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
@@ -90,17 +100,17 @@ public class SubscriptionWebhookServlet extends SailingServerHttpServlet {
|
||||
response.setStatus(500);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Subscription buildEmptySubscription(Subscription currentSubscription, SubscriptionWebhookEvent event) {
|
||||
Subscription subscription = new Subscription();
|
||||
subscription.latestEventTime = event.getEventOccurredAt();
|
||||
subscription.manualUpdatedAt = currentSubscription != null ? currentSubscription.manualUpdatedAt : 0;
|
||||
return subscription;
|
||||
}
|
||||
|
||||
|
||||
private Subscription buildSubscription(Subscription currentSubscription, SubscriptionWebhookEvent event) {
|
||||
Subscription subscription = new Subscription();
|
||||
|
||||
|
||||
subscription.subscriptionId = event.getSubscriptionId();
|
||||
subscription.planId = event.getPlanId();
|
||||
subscription.customerId = event.getCustomerId();
|
||||
@@ -111,8 +121,9 @@ public class SubscriptionWebhookServlet extends SailingServerHttpServlet {
|
||||
subscription.subsciptionUpdatedAt = event.getSubscriptionUpdatedAt();
|
||||
subscription.latestEventTime = event.getEventOccurredAt();
|
||||
subscription.manualUpdatedAt = currentSubscription != null ? currentSubscription.manualUpdatedAt : 0;
|
||||
|
||||
if (subscription.subscriptionStatus != null && subscription.subscriptionStatus.equals(SubscriptionWebhookEvent.SUBSCRIPTION_STATUS_ACTIVE)) {
|
||||
|
||||
if (subscription.subscriptionStatus != null
|
||||
&& subscription.subscriptionStatus.equals(SubscriptionWebhookEvent.SUBSCRIPTION_STATUS_ACTIVE)) {
|
||||
String paymentStatus = getEventPaymentStatus(event);
|
||||
if (paymentStatus != null) {
|
||||
subscription.paymentStatus = paymentStatus;
|
||||
@@ -122,52 +133,53 @@ public class SubscriptionWebhookServlet extends SailingServerHttpServlet {
|
||||
} else {
|
||||
subscription.paymentStatus = null;
|
||||
}
|
||||
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
|
||||
private User getUser(String customerId) {
|
||||
return getSecurityService().getUserByName(customerId);
|
||||
}
|
||||
|
||||
|
||||
private void updateUserSubscription(User user, Subscription subscription) throws UserManagementException {
|
||||
getSecurityService().updateUserSubscription(user.getName(), subscription);
|
||||
}
|
||||
|
||||
|
||||
private String getEventPaymentStatus(SubscriptionWebhookEvent event) {
|
||||
String paymentStatus = null;
|
||||
String transactionStatus = event.getTransactionStatus();
|
||||
if (transactionStatus == null) {
|
||||
String invoiceStatus = event.getInvoiceStatus();
|
||||
if (invoiceStatus != null) {
|
||||
paymentStatus = invoiceStatus.equals(SubscriptionWebhookEvent.INVOICE_STATUS_PAID) ?
|
||||
Subscription.PAYMENT_STATUS_SUCCESS :
|
||||
Subscription.PAYMENT_STATUS_NO_SUCCESS;
|
||||
paymentStatus = invoiceStatus.equals(SubscriptionWebhookEvent.INVOICE_STATUS_PAID)
|
||||
? Subscription.PAYMENT_STATUS_SUCCESS
|
||||
: Subscription.PAYMENT_STATUS_NO_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
String transactionType = event.getTransactionType();
|
||||
if (transactionType != null && transactionType.equals(SubscriptionWebhookEvent.TRANSACTION_TYPE_PAYMENT)) {
|
||||
paymentStatus = transactionStatus.equals(SubscriptionWebhookEvent.TRANSACTION_STATUS_SUCCESS) ?
|
||||
Subscription.PAYMENT_STATUS_SUCCESS :
|
||||
Subscription.PAYMENT_STATUS_NO_SUCCESS;
|
||||
paymentStatus = transactionStatus.equals(SubscriptionWebhookEvent.TRANSACTION_STATUS_SUCCESS)
|
||||
? Subscription.PAYMENT_STATUS_SUCCESS
|
||||
: Subscription.PAYMENT_STATUS_NO_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return paymentStatus;
|
||||
}
|
||||
|
||||
|
||||
private boolean verifyBasicAuth(HttpServletRequest request) {
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
if (authHeader == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (basicAuthHeaderValue == null) {
|
||||
String cred = WebhookBasicAuthConfiguration.getInstance().getUsername() + ":" + WebhookBasicAuthConfiguration.getInstance().getPassword();
|
||||
String cred = WebhookBasicAuthConfiguration.getInstance().getUsername() + ":"
|
||||
+ WebhookBasicAuthConfiguration.getInstance().getPassword();
|
||||
String base64Hash = Base64.getEncoder().encodeToString(cred.getBytes());
|
||||
basicAuthHeaderValue = "Basic " + base64Hash;
|
||||
}
|
||||
|
||||
|
||||
return authHeader.equals(basicAuthHeaderValue);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-9
@@ -1,25 +1,28 @@
|
||||
package com.sap.sailing.server.gateway.subscription;
|
||||
|
||||
/**
|
||||
* Chargebee webhook basic authentication configuration. The basic auth user and password have to be in server
|
||||
* application start arguments: chargebee.basicauthuser, chargebee.basicauthpass
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class WebhookBasicAuthConfiguration {
|
||||
private static final String USER = "chargebee.basicauthuser";
|
||||
private static final String PASSWORD = "chargebee.basicauthpass";
|
||||
|
||||
|
||||
private static WebhookBasicAuthConfiguration instance;
|
||||
|
||||
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
|
||||
public static WebhookBasicAuthConfiguration getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WebhookBasicAuthConfiguration(
|
||||
System.getProperty(USER),
|
||||
System.getProperty(PASSWORD)
|
||||
);
|
||||
instance = new WebhookBasicAuthConfiguration(System.getProperty(USER), System.getProperty(PASSWORD));
|
||||
}
|
||||
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
public WebhookBasicAuthConfiguration(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
|
||||
@@ -2,6 +2,11 @@ package com.sap.sse.security.shared;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Subscription data model for user which is persisted into database as subscription property of a user
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class Subscription implements Serializable {
|
||||
public static String PAYMENT_STATUS_SUCCESS = "success";
|
||||
public static String PAYMENT_STATUS_NO_SUCCESS = "no_success";
|
||||
@@ -11,15 +16,69 @@ public class Subscription implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 96845123954667808L;
|
||||
|
||||
/**
|
||||
* Subscription id from Chargebee
|
||||
*/
|
||||
public String subscriptionId;
|
||||
|
||||
/**
|
||||
* Current subscription plan id
|
||||
*/
|
||||
public String planId;
|
||||
|
||||
/**
|
||||
* Chargebee's customer id, this is same as the system's user name
|
||||
*/
|
||||
public String customerId;
|
||||
|
||||
/**
|
||||
* Subscription trial start timestamp
|
||||
*/
|
||||
public long trialStart;
|
||||
|
||||
/**
|
||||
* Subscription trial end timestamp
|
||||
*/
|
||||
public long trialEnd;
|
||||
|
||||
/**
|
||||
* Subscription status, it could be in_trial, active, or cancelled.
|
||||
* in_tiral means the subscription is in trial period
|
||||
* active means the subscription is active
|
||||
* cancelled means the subscription has been cancelled(by user or by admin from Chargebee dashboard)
|
||||
*/
|
||||
public String subscriptionStatus;
|
||||
|
||||
/**
|
||||
* Subscription payment status, it records if user has successfully paid for the subscription.
|
||||
* User will pay for the subscription only if the subscription is turned to active(after trial period)
|
||||
* If user has successfully paid for the subscription, this has value "success", otherwise "no_success"
|
||||
*/
|
||||
public String paymentStatus;
|
||||
|
||||
/**
|
||||
* Record the creating timestamp of the Chargebee's subscription
|
||||
*/
|
||||
public long subsciptionCreatedAt;
|
||||
|
||||
/**
|
||||
* Record the updating timestamp of the Chargebee's subscription
|
||||
*/
|
||||
public long subsciptionUpdatedAt;
|
||||
|
||||
/**
|
||||
* Record the timestamp of the latest handled webhook event.
|
||||
* Because a webhook event might be retried to send to our system by Chargebee later if it has failed on the prev times,
|
||||
* so we will this timestamp to only process a newer event, otherwise we will update wrong data for a user.
|
||||
*
|
||||
* {@link https://www.chargebee.com/docs/webhook_settings.html#automatic-retries}
|
||||
*/
|
||||
public long latestEventTime;
|
||||
|
||||
/**
|
||||
* Record the timestamp the subscription was updated by user using the system, like changing plan or cancel subscription.
|
||||
* Reason is Chargebee will retried to send us failed webhook events, so with this timestamp we'll process only webhook events
|
||||
* occur after this timestamp, otherwise we'll update with old data.
|
||||
*/
|
||||
public long manualUpdatedAt;
|
||||
}
|
||||
|
||||
@@ -683,6 +683,13 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
|
||||
void registerCustomizer(SecurityInitializationCustomizer customizer);
|
||||
|
||||
/**
|
||||
* Persist user subscription data
|
||||
*
|
||||
* @param username
|
||||
* @param subscription
|
||||
* @throws UserManagementException
|
||||
*/
|
||||
void updateUserSubscription(String username, Subscription subscription) throws UserManagementException;
|
||||
|
||||
}
|
||||
|
||||
+5
@@ -3,6 +3,11 @@ package com.sap.sse.security.operations;
|
||||
import com.sap.sse.security.impl.ReplicableSecurityService;
|
||||
import com.sap.sse.security.shared.Subscription;
|
||||
|
||||
/**
|
||||
* Update user's subscription operation
|
||||
*
|
||||
* @author tutran
|
||||
*/
|
||||
public class UpdateUserSubscriptionOperation implements SecurityOperation<Void> {
|
||||
private static final long serialVersionUID = 4943500215851172841L;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user