diff --git a/docs/inline-custom.png b/docs/inline-custom.png index 45b2f35..d9b6599 100644 Binary files a/docs/inline-custom.png and b/docs/inline-custom.png differ diff --git a/docs/inline-ecom-checkout.png b/docs/inline-ecom-checkout.png index 6ee3e5b..a74c4bc 100644 Binary files a/docs/inline-ecom-checkout.png and b/docs/inline-ecom-checkout.png differ diff --git a/lib/inline.js b/lib/inline.js index 34c6017..63e7569 100644 --- a/lib/inline.js +++ b/lib/inline.js @@ -23,7 +23,7 @@ class ChapaCheckout { onClose: options.onClose || null, }; - this.paymentType = ""; + this.paymentType = this.options.availablePaymentMethods[0] ?? ''; this.hostedUrl = "https://api.chapa.co/v1/hosted/pay"; this.chapaUrl = "https://inline.chapaservices.net/v1/inline/charge"; this.verifyUrl = "https://inline.chapaservices.net/v1/inline/validate"; @@ -66,8 +66,8 @@ class ChapaCheckout { } container.innerHTML = ` -
+
@@ -81,6 +81,18 @@ class ChapaCheckout { this.renderPayButton(); this.applyCustomStyles(); } + + validatePhoneNumberOnInput(e) { + const phoneNumber = e.target.value; + const mobileRegex = /^(251\d{9}|0\d{9}|9\d{8}|7\d{8})$/; + if (!mobileRegex.test(phoneNumber)) { + this.showError("Please enter a valid Ethiopian phone number."); + return false; + }else{ + this.hideError(); + } + return true; + } renderPhoneInput() { const inputContainer = document.getElementById( @@ -102,21 +114,40 @@ class ChapaCheckout { +251
` } - +
+ + + `; + + // add phone number input + + const phoneWrapper = document.getElementById("phone-input-container"); + + + const phoneInput = document.createElement("input"); + phoneInput.id = "chapa-phone-number"; + phoneInput.className = "chapa-phone-input"; + phoneInput.type = "tel"; + phoneInput.placeholder = "9|7XXXXXXXX"; + phoneInput.value = this.options.mobile; + phoneInput.addEventListener("input", (e) => this.validatePhoneNumberOnInput(e)); + phoneWrapper.appendChild(phoneInput); } handlePayment() { - const phoneNumber = - this.options.mobile || - document.getElementById("chapa-phone-number").value; + const phoneNumber = document.getElementById("chapa-phone-number").value; + + if ( !this.validatePhoneNumber(phoneNumber) || !this.validatePaymentMethod() ) { + return; } @@ -147,6 +178,9 @@ class ChapaCheckout { const paymentMethod = this.paymentMethodIcons[method]; const element = document.createElement("div"); element.className = "chapa-payment-method"; + if (method === this.paymentType) { + element.classList.add("chapa-selected"); + } element.innerHTML = ` ${paymentMethod.name} ${ @@ -181,26 +215,28 @@ class ChapaCheckout { const style = document.createElement("style"); style.id = "chapa-styles"; style.textContent = ` - .chapa-error { display: none; color: red; margin-bottom: 10px; } + .chapa-error { display: none; color: red; margin-bottom: 10px; margin-top: 10px; } .chapa-loading { display: none; text-align: center; margin-top: 15px; } .chapa-spinner { display: inline-block; width: 30px; height: 30px; border: 3px solid rgba(0,0,0,.1); border-radius: 50%; border-top-color: #7DC400; animation: chapa-spin 1s ease-in-out infinite; } @keyframes chapa-spin { to { transform: rotate(360deg); } } - .chapa-payment-methods-grid { display: flex; justify-content: space-between; gap: 8px; margin: 15px 0; } + .chapa-payment-methods-grid { display: flex; gap: 8px; margin: 15px 0; justify-content: space-between; } .chapa-payment-method { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 8px; border: 1px solid #e5e7eb; border-radius: 4px; cursor: pointer; width: 60px; height: 60px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15); } .chapa-payment-icon { width: 42px; height: 42px; margin-bottom: 4px; } .chapa-payment-name { font-size: 11px; text-align: center; } - .chapa-selected { background-color: #e6fffa; box-shadow: 0 0 0 2px #10b981; } - .chapa-input-wrapper { margin-bottom: 10px; } + .chapa-selected { background-color: #7dc40024; box-shadow: 0 0 0 1px #7DC400; } + .chapa-input-wrapper { margin-bottom: 10px; ] } .chapa-input-wrapper label { display: block; margin-bottom: 5px; font-weight: 600; color: #333; } .chapa-input { width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 16px; outline: none; box-sizing: border-box; transition: border-color 0.3s, box-shadow 0.3s; } - .chapa-input:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2); } - .chapa-phone-input-wrapper { position: relative; margin-bottom: 20px; display: flex; align-items: center; } + .chapa-input:focus { border-color: #7DC400; box-shadow: 0 0 0 3px #7dc40024; } + .chapa-phone-input-wrapper { position: relative; margin-bottom: 20px; display: flex; align-items: center; border: 1px solid #d1d5db; border-radius: 8px; padding: 8px 12px; } .chapa-phone-prefix { display: flex; align-items: center; padding: 0 12px; background-color: #ffffff; border-radius: 7px 0 0 7px; height: 100%; font-size: 16px; color: #6b7280; } .chapa-flag-icon { width: 24px; height: auto; margin-right: 8px; } - .chapa-phone-input { width: 100%; padding: 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: 16px; outline: none; box-sizing: border-box; transition: border-color 0.3s, box-shadow 0.3s; } - .chapa-phone-input:focus { border-color: #10b981; box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.2); } + .chapa-phone-input { width: 100%; padding: 10px; border: none; border-left: 1px solid #d1d5db; font-size: 18px; outline: none !important; box-shadow: none !important; box-sizing: border-box; transition: border-color 0.3s, box-shadow 0.3s; } + .chapa-phone-input-wrapper:hover { border-color: #7DC400; box-shadow: 0 0 0 3px #7dc40024; } + .chapa-phone-input-wrapper:hover .chapa-phone-input { border-color: #7DC400; box-shadow: 0 0 0 3px #7dc40024; } .chapa-pay-button { background-color: #7DC400; color: #FFFFFF; border: none; border-radius: 4px; padding: 10px; font-size: 16px; cursor: pointer; width: 100%; transition: background-color 0.3s; } .chapa-pay-button:hover { background-color: #6baf00; } + #phone-input-container { width: 100%; } `; document.head.appendChild(style); } @@ -211,44 +247,39 @@ class ChapaCheckout { document.head.appendChild(customStyle); } - this.adjustPhoneInputPadding(); - window.addEventListener("resize", () => this.adjustPhoneInputPadding()); - } - - adjustPhoneInputPadding() { - const prefix = document.querySelector(".chapa-phone-prefix"); - const input = document.querySelector(".chapa-phone-input"); - if (prefix && input) { - const prefixWidth = prefix.offsetWidth; - input.style.paddingLeft = `${prefixWidth + 12}px`; - } + } + validatePhoneNumber(phoneNumber) { - if (phoneNumber.startsWith("0")) { - phoneNumber = phoneNumber.substring(1); - } else if (phoneNumber.startsWith("251")) { - phoneNumber = phoneNumber.substring(3); - } else if (phoneNumber.startsWith("+251")) { - phoneNumber = phoneNumber.substring(4); - } - - const firstDigit = phoneNumber.charAt(0); - if ( - phoneNumber.length !== 9 || - (firstDigit !== "9" && firstDigit !== "7") - ) { - this.showError("Please enter a valid Ethiopian phone number."); + const mobileRegex = /^(251\d{9}|0\d{9}|9\d{8}|7\d{8})$/; + if (!mobileRegex.test(phoneNumber)) { + this.showError("Please enter a valid Phone Number."); return false; } - if (this.paymentType === "telebirr" && firstDigit !== "9") { - this.showError("Please enter a valid telebirr mobile number."); + + + if(phoneNumber.charAt(0) === '0'){ + phoneNumber = phoneNumber.slice(1); + } + const telebirrRegex = /^(2519\d{8}|9\d{8})$/; + + if (this.paymentType === "telebirr" && telebirrRegex.test(phoneNumber) === false) { + this.showError("Please enter a valid Telebirr Phone Number."); return false; + } - if (this.paymentType === "mpesa" && firstDigit !== "7") { - this.showError("Please enter a valid M-Pesa mobile number."); + + const mpesaRegex = /^(2519\d{8}|7\d{8})$/; + + if (this.paymentType === "mpesa" && mpesaRegex.test(phoneNumber) === false) { + this.showError("Please enter a valid Mpesa Phone Number."); return false; + } + + + return true; }