2024-10-06 23:56:03 +02:00
|
|
|
import {
|
|
|
|
|
customElement,
|
|
|
|
|
DeesElement,
|
|
|
|
|
property,
|
|
|
|
|
html,
|
|
|
|
|
cssManager,
|
|
|
|
|
unsafeCSS,
|
|
|
|
|
css,
|
|
|
|
|
} from '@design.estate/dees-element';
|
|
|
|
|
|
|
|
|
|
import * as plugins from '../../../plugins.js';
|
2025-12-22 15:56:20 +00:00
|
|
|
import * as sharedStyles from '../sharedstyles.js';
|
2024-10-06 23:56:03 +02:00
|
|
|
import * as state from '../../../states/accountstate.js';
|
2025-12-07 20:45:30 +00:00
|
|
|
import { IdpState } from '../../../states/idp.state.js';
|
2024-10-06 23:56:03 +02:00
|
|
|
|
|
|
|
|
declare global {
|
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
|
'lele-accountview-paddlesetup': PaddleSetupView;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@customElement('lele-accountview-paddlesetup')
|
|
|
|
|
export class PaddleSetupView extends DeesElement {
|
|
|
|
|
public static styles = [
|
|
|
|
|
cssManager.defaultStyles,
|
2025-12-22 15:56:20 +00:00
|
|
|
sharedStyles.accountDesignTokens,
|
|
|
|
|
sharedStyles.viewBaseStyles,
|
2024-10-06 23:56:03 +02:00
|
|
|
css`
|
|
|
|
|
:host {
|
2025-12-22 15:56:20 +00:00
|
|
|
padding: 48px;
|
2024-10-06 23:56:03 +02:00
|
|
|
max-width: 900px;
|
2025-12-22 15:56:20 +00:00
|
|
|
margin: 0 auto;
|
2024-10-06 23:56:03 +02:00
|
|
|
}
|
|
|
|
|
`,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
public render() {
|
|
|
|
|
return html`
|
|
|
|
|
<h1>-> Paddle Setup</h1>
|
|
|
|
|
<p>
|
|
|
|
|
In order to use workspace.global <b>with paid features</b>, you need to setup a Paddle
|
|
|
|
|
subscription. A Paddle connection is bound to an organization.
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
The base price of a Paddle Subscription is always 0€. Any charges that occur will be billed
|
|
|
|
|
as an extra charge on top of your free base subscription
|
|
|
|
|
<b>on a monthly date of your choosing</b>.
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
Since Paddle acts as merchant of record, your invoices will read Paddle as Creditor, and you
|
|
|
|
|
as Debitor.
|
|
|
|
|
</p>
|
|
|
|
|
<h2>Why are we using Paddle?</h2>
|
|
|
|
|
<p>
|
|
|
|
|
Paddle takes care of tax compliance for us. This allows us to sell our products world wide
|
|
|
|
|
while Paddle makes sure any sales are in compliance with local laws.
|
|
|
|
|
</p>
|
2025-12-01 04:44:47 +00:00
|
|
|
<dees-button @clicked=${() => this.openPaddle()}>Let's do it!</dees-button>
|
2024-10-06 23:56:03 +02:00
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-01 04:44:47 +00:00
|
|
|
public async openPaddle() {
|
2024-10-06 23:56:03 +02:00
|
|
|
await this.domtoolsPromise;
|
|
|
|
|
const paddleButton = this.shadowRoot.querySelector('dees-button');
|
2025-12-07 20:45:30 +00:00
|
|
|
const idpState = await IdpState.getSingletonInstance();
|
|
|
|
|
|
|
|
|
|
// Get user email - first try from state, then fetch directly
|
|
|
|
|
let userEmail = state.accountState.getState().user?.data?.email;
|
|
|
|
|
|
|
|
|
|
if (!userEmail) {
|
|
|
|
|
// State not loaded, fetch user directly
|
|
|
|
|
const whoIsResponse = await idpState.idpClient.whoIs().catch(() => null);
|
|
|
|
|
userEmail = whoIsResponse?.user?.data?.email;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!userEmail) {
|
|
|
|
|
console.error('Unable to get user email for Paddle checkout');
|
|
|
|
|
paddleButton.status = 'error';
|
|
|
|
|
paddleButton.text = 'Error: Not logged in';
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch Paddle config from backend
|
|
|
|
|
const configRequest = idpState.idpClient.typedsocket
|
|
|
|
|
.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPaddleConfig>('getPaddleConfig');
|
|
|
|
|
const { paddleToken, paddlePriceId } = await configRequest.fire({});
|
|
|
|
|
|
|
|
|
|
await this.domtools.setExternalScript('https://cdn.paddle.com/paddle/v2/paddle.js');
|
|
|
|
|
globalThis.Paddle.Initialize({
|
|
|
|
|
token: paddleToken,
|
2025-12-01 04:44:47 +00:00
|
|
|
eventCallback: async (dataArg: any) => {
|
2025-12-07 20:45:30 +00:00
|
|
|
// Paddle Billing v2 event handling
|
|
|
|
|
if (dataArg.name === 'checkout.completed') {
|
2025-12-01 04:44:47 +00:00
|
|
|
const paddleIframe = document.body.querySelector('iframe');
|
|
|
|
|
if (paddleIframe) {
|
2024-10-06 23:56:03 +02:00
|
|
|
document.body.removeChild(paddleIframe);
|
|
|
|
|
}
|
2025-12-01 04:44:47 +00:00
|
|
|
paddleButton.status = 'pending';
|
|
|
|
|
paddleButton.text = 'Processing...';
|
2025-12-07 20:45:30 +00:00
|
|
|
await state.accountState.dispatchAction(state.updatePaddleCheckoutId, dataArg.data.transaction_id);
|
2025-12-01 04:44:47 +00:00
|
|
|
paddleButton.status = 'success';
|
|
|
|
|
paddleButton.text = 'Paddle connected!'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
globalThis.Paddle.Checkout.open({
|
2025-12-07 20:45:30 +00:00
|
|
|
items: [{ priceId: paddlePriceId, quantity: 1 }],
|
|
|
|
|
customer: { email: userEmail },
|
2024-10-06 23:56:03 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|