@@ -593,6 +593,108 @@ export class Lib {
593593 this . cachedData = null
594594 }
595595
596+ /**
597+ * Gets the anonymous profile ID for the current visitor.
598+ * If profileId was set via init options, returns that.
599+ * Otherwise, requests server to generate one from IP/UA hash.
600+ *
601+ * This ID can be used for revenue attribution with payment providers.
602+ *
603+ * @returns A promise that resolves to the profile ID string, or null on error.
604+ *
605+ * @example
606+ * ```typescript
607+ * const profileId = await swetrix.getProfileId()
608+ *
609+ * // Pass to Paddle Checkout for revenue attribution
610+ * Paddle.Checkout.open({
611+ * items: [{ priceId: 'pri_01234567890', quantity: 1 }],
612+ * customData: {
613+ * swetrix_profile_id: profileId,
614+ * swetrix_session_id: await swetrix.getSessionId()
615+ * }
616+ * })
617+ * ```
618+ */
619+ async getProfileId ( ) : Promise < string | null > {
620+ // If profileId is already set in options, return it
621+ if ( this . options ?. profileId ) {
622+ return this . options . profileId
623+ }
624+
625+ if ( ! isInBrowser ( ) ) {
626+ return null
627+ }
628+
629+ try {
630+ const apiBase = this . getApiBase ( )
631+ const response = await fetch ( `${ apiBase } /log/profile-id` , {
632+ method : 'POST' ,
633+ headers : {
634+ 'Content-Type' : 'application/json' ,
635+ } ,
636+ body : JSON . stringify ( { pid : this . projectID } ) ,
637+ } )
638+
639+ if ( ! response . ok ) {
640+ return null
641+ }
642+
643+ const data = ( await response . json ( ) ) as { profileId : string | null }
644+ return data . profileId
645+ } catch {
646+ return null
647+ }
648+ }
649+
650+ /**
651+ * Gets the current session ID for the visitor.
652+ * Session IDs are generated server-side based on IP and user agent.
653+ *
654+ * This ID can be used for revenue attribution with payment providers.
655+ *
656+ * @returns A promise that resolves to the session ID string, or null on error.
657+ *
658+ * @example
659+ * ```typescript
660+ * const sessionId = await swetrix.getSessionId()
661+ *
662+ * // Pass to Paddle Checkout for revenue attribution
663+ * Paddle.Checkout.open({
664+ * items: [{ priceId: 'pri_01234567890', quantity: 1 }],
665+ * customData: {
666+ * swetrix_profile_id: await swetrix.getProfileId(),
667+ * swetrix_session_id: sessionId
668+ * }
669+ * })
670+ * ```
671+ */
672+ async getSessionId ( ) : Promise < string | null > {
673+ if ( ! isInBrowser ( ) ) {
674+ return null
675+ }
676+
677+ try {
678+ const apiBase = this . getApiBase ( )
679+ const response = await fetch ( `${ apiBase } /log/session-id` , {
680+ method : 'POST' ,
681+ headers : {
682+ 'Content-Type' : 'application/json' ,
683+ } ,
684+ body : JSON . stringify ( { pid : this . projectID } ) ,
685+ } )
686+
687+ if ( ! response . ok ) {
688+ return null
689+ }
690+
691+ const data = ( await response . json ( ) ) as { sessionId : string | null }
692+ return data . sessionId
693+ } catch {
694+ return null
695+ }
696+ }
697+
596698 /**
597699 * Gets the API base URL (without /log suffix).
598700 */
0 commit comments