subscriptions in progress

This commit is contained in:
tavo-wasd 2024-08-19 01:44:39 -06:00
parent 59ea41ed74
commit 38c6523fbf
3 changed files with 179 additions and 4 deletions

117
main.go
View file

@ -28,6 +28,47 @@ type OrderData struct {
} `json:"purchase_units"` } `json:"purchase_units"`
} }
type SubscriptionData struct {
ID string `json:"id"`
Status string `json:"status"`
// StatusUpdateTime time.Time `json:"status_update_time"`
PlanID string `json:"plan_id"`
PlanOverridden bool `json:"plan_overridden"`
// StartTime time.Time `json:"start_time"`
Quantity string `json:"quantity"`
ShippingAmount struct {
CurrencyCode string `json:"currency_code"`
Value string `json:"value"`
} `json:"shipping_amount"`
Subscriber struct {
Name struct {
GivenName string `json:"given_name"`
Surname string `json:"surname"`
} `json:"name"`
EmailAddress string `json:"email_address"`
PayerID string `json:"payer_id"`
ShippingAddress struct {
Name struct {
FullName string `json:"full_name"`
} `json:"name"`
Address struct {
AddressLine1 string `json:"address_line_1"`
AddressLine2 string `json:"address_line_2"`
AdminArea2 string `json:"admin_area_2"`
AdminArea1 string `json:"admin_area_1"`
PostalCode string `json:"postal_code"`
CountryCode string `json:"country_code"`
} `json:"address"`
} `json:"shipping_address"`
} `json:"subscriber"`
// CreateTime time.Time `json:"create_time"`
Links []struct {
Href string `json:"href"`
Rel string `json:"rel"`
Method string `json:"method"`
} `json:"links"`
}
var ( var (
baseURL = "https://api-m.sandbox.paypal.com" baseURL = "https://api-m.sandbox.paypal.com"
) )
@ -42,6 +83,7 @@ func main() {
// Handlers // Handlers
http.HandleFunc("/api/orders", CreateOrder) http.HandleFunc("/api/orders", CreateOrder)
http.HandleFunc("/api/orders/", CaptureOrder) http.HandleFunc("/api/orders/", CaptureOrder)
http.HandleFunc("/api/paypal/create-subscription", CreateSubscription)
http.Handle("/", http.FileServer(http.Dir("./public"))) http.Handle("/", http.FileServer(http.Dir("./public")))
// Channel to listen for signals // Channel to listen for signals
@ -178,5 +220,80 @@ func CaptureOrder(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(result); err != nil { if err := json.NewEncoder(w).Encode(result); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError) http.Error(w, "Failed to encode response", http.StatusInternalServerError)
return
} }
} }
func CreateSubscription(w http.ResponseWriter, r *http.Request) {
log.Printf("asked to create sub")
planID := os.Getenv("PLAN_ID")
returnUrl := "https://suckless.org"
cancelUrl := "https://suckless.org"
log.Printf("This is the planid: %s", planID)
token, err := Token()
if err != nil {
http.Error(w, "Failed to get access token", http.StatusInternalServerError)
return
}
log.Printf("This is the token: %s", token)
body := map[string]interface{}{
"plan_id": planID,
"application_context": map[string]string{
"shipping_preference": "NO_SHIPPING",
"return_url": returnUrl,
"cancel_url": cancelUrl,
},
}
jsonData, err := json.Marshal(body)
if err != nil {
http.Error(w, "Server error", http.StatusInternalServerError)
return
}
log.Printf("Creating request")
req, err := http.NewRequest("POST", baseURL+"/v1/billing/subscriptions", bytes.NewBuffer(jsonData))
if err != nil {
http.Error(w, "Failed to create request", http.StatusInternalServerError)
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Accept", "application/json")
req.Header.Set("Prefer", "return=representation")
log.Printf("Sending request")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Failed to send request", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
log.Printf("Request sent")
// Create an instance of AutoGenerated
// var result SubscriptionData
var result map[string]interface{}
// Decode the response into the AutoGenerated struct
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
http.Error(w, "Failed to decode response", http.StatusInternalServerError)
return
}
log.Printf("Raw JSON Response: %v", result)
// Now, `result` contains the entire structured response
// You can send the whole `result` back to the client, or you can selectively send fields.
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(result); err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
}
log.Printf("sent response to client")
}

View file

@ -145,6 +145,63 @@ window.paypal_order.Buttons({
}, },
}).render("#paypal-button-container-order"); }).render("#paypal-button-container-order");
window.paypal_subscribe.Buttons({
style: { shape: 'pill', color: 'black', layout: 'vertical', label: 'subscribe' },
async createSubscription() {
try {
const response = await fetch("/api/paypal/create-subscription", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ userAction: "SUBSCRIBE_NOW" }),
});
const data = await response.json();
if (data?.id) {
resultMessage(`Successful subscription with ID ${data.id}...<br><br>`);
return data.id;
} else {
console.error(
{ callback: "createSubscription", serverResponse: data },
JSON.stringify(data, null, 2),
);
// (Optional) The following hides the button container and shows a message about why checkout can't be initiated
const errorDetail = data?.details?.[0];
resultMessage(
`Could not initiate PayPal Subscription...<br><br>${
errorDetail?.issue || ""
} ${errorDetail?.description || data?.message || ""} ` +
(data?.debug_id ? `(${data.debug_id})` : ""),
{ hideButtons: true },
);
}
const approvalUrl = data.links.find(link => link.rel === "approve").href;
window.location.href = approvalUrl;
} catch (error) {
console.error(error);
resultMessage(
`Could not initiate PayPal Subscription...<br><br>${error}`,
);
}
},
onApprove(data) {
/*
No need to activate manually since SUBSCRIBE_NOW is being used.
Learn how to handle other user actions from our docs:
https://developer.paypal.com/docs/api/subscriptions/v1/#subscriptions_create
*/
if (data.orderID) {
resultMessage(
`You have successfully subscribed to the plan. Your subscription id is: ${data.subscriptionID}`,
);
} else {
resultMessage(
`Failed to activate the subscription: ${data.subscriptionID}`,
);
}
},
}).render("#paypal-button-container-subscribe"); // Renders the PayPal button
// Example function to show a result to the user. Your site's UI library can be used instead. // Example function to show a result to the user. Your site's UI library can be used instead.
function resultMessage(message) { function resultMessage(message) {
const container = document.querySelector("#checkout"); const container = document.querySelector("#checkout");

View file

@ -29,10 +29,10 @@
src="https://www.paypal.com/sdk/js?client-id=AcCW43LI1S6lLQgtLkF4V8UOPfmXcqXQ8xfEl41hRuMxSskR2jkWNwQN6Ab1WK7E2E52GNaoYBHqgIKd&components=buttons&enable-funding=card&disable-funding=paylater,venmo" src="https://www.paypal.com/sdk/js?client-id=AcCW43LI1S6lLQgtLkF4V8UOPfmXcqXQ8xfEl41hRuMxSskR2jkWNwQN6Ab1WK7E2E52GNaoYBHqgIKd&components=buttons&enable-funding=card&disable-funding=paylater,venmo"
data-namespace="paypal_order" data-namespace="paypal_order"
></script> ></script>
<!-- <script --> <script
<!-- src="" --> src="https://www.paypal.com/sdk/js?client-id=AcCW43LI1S6lLQgtLkF4V8UOPfmXcqXQ8xfEl41hRuMxSskR2jkWNwQN6Ab1WK7E2E52GNaoYBHqgIKd&components=buttons&vault=true&intent=subscription&enable-funding=card&disable-funding=paylater"
<!-- data-namespace="paypal_subscribe" --> data-namespace="paypal_subscribe"
<!-- ></script> --> ></script>
</head> </head>
<form action="submit.php" id="mainForm" method="post"> <form action="submit.php" id="mainForm" method="post">
<div class="banner" style="background-image: url(/static/banner.jpg);"> <div class="banner" style="background-image: url(/static/banner.jpg);">
@ -82,6 +82,7 @@
</div> </div>
<div id="paypal-button-container"> <div id="paypal-button-container">
<div id="paypal-button-container-order"></div> <div id="paypal-button-container-order"></div>
<div id="paypal-button-container-subscribe"></div>
</div> </div>
</div> </div>
</div> </div>