Start typing to search packages!
purchase-result-modal
By @biotoxin495
Roblox
Mirrored from WallyPurchaseResultModal โ Animated purchase result UI for Roblox
PurchaseResultModal is a lightweight client-side Luau module for showing animated success or failure feedback after a purchase flow resolves.
The module creates a result card at runtime, animates its status icon and gradient, and closes it automatically or in response to player input. It presents the result your game provides; it does not determine whether a transaction succeeded.
Quick example
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PurchaseResultModal = require(
ReplicatedStorage.Packages.PurchaseResultModal
)
local modal = PurchaseResultModal.new()
if purchaseSucceeded then
modal:ShowSuccess()
else
modal:ShowFailure()
end
-- Reuse the modal for later results. Call modal:Destroy() when it is no longer needed.
When no Parent is supplied, the generated ScreenGui is placed in the local player's PlayerGui.
๐ Features
- Show success and failure results with separate icons and color gradients
- Animate the result card, status icon, and gradient shine
- Automatically close after a configurable delay
- Allow click or tap dismissal
- Override the auto-close duration for each presentation
- Replace an active result safely without stale timers closing the new one
- Respect Roblox reduced-motion settings
- No external runtime dependencies
๐ ๏ธ Installation
ModuleScript
Place init.luau in a client-accessible ModuleScript, for example:
ReplicatedStorage
โโโ Packages
โโโ PurchaseResultModal
โโโ init.luau
Require and construct it from a LocalScript:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PurchaseResultModal = require(
ReplicatedStorage:WaitForChild("Packages"):WaitForChild("PurchaseResultModal")
)
local modal = PurchaseResultModal.new()
Wally
Add the package to your project's wally.toml:
[dependencies]
PurchaseResultModal = "biotoxin495/purchase-result-modal@1.0.0"
Construct the controller on the client.
Basic usage
Pass the result from your own purchase flow to the modal:
if purchaseSucceeded then
modal:ShowSuccess()
else
modal:ShowFailure()
end
You can choose the time a result stays open for one presentation:
modal:ShowSuccess({
AutoCloseDuration = 5,
})
Set the duration to 0 to leave the result open until the player dismisses it or your code calls Close():
modal:ShowFailure({
AutoCloseDuration = 0,
})
The package does not call MarketplaceService, listen to remotes, or decide whether a purchase succeeded. Do not show a failure result when a player merely cancels a purchase prompt unless that is the experience you want.
API reference
PurchaseResultModal.new(config?) -> PurchaseResultModal
Creates the controller and its generated ScreenGui. The UI is parented to config.Parent when provided, or to the local player's PlayerGui by default. Create the controller on the client.
Configuration
All fields are optional.
| Option | Type | Default | Description |
|---|---|---|---|
Name | string | "PurchaseResultModal" | Generated ScreenGui name. |
Parent | Instance? | Local PlayerGui | Parent of the generated UI. |
DisplayOrder | number | 110 | Generated ScreenGui.DisplayOrder. |
AutoCloseDuration | number | 3 | How long a result remains open. Use 0 to disable automatic closing. |
AutoCloseStartDelay | number | 0.3 | Delay before the auto-close timer starts. |
DismissOnInput | boolean | true | Enables click/tap dismissal. |
RespectReducedMotion | boolean | true | Reduces movement-heavy effects when reduced motion is enabled. |
OpenDuration | number | 0.35 | Card opening tween duration. |
CloseDuration | number | 0.35 | Card closing tween duration. |
IconDuration | number | 0.6 | Icon fade and rotation duration. |
IconDelay | number | 0.1 | Delay before the icon animation starts. |
ShineDuration | number | 1 | Duration of one gradient sweep. |
ShinesPerCycle | number | 2 | Consecutive sweeps in each shine cycle. |
ShineInterval | number | 2.5 | Pause between shine cycles. |
ModalSize | UDim2 | UDim2.new(0.25, 0, 0.4, 0) | Result card size. |
OpenPosition | UDim2 | Screen center | Result card position while open. |
OpenAnchorPoint | Vector2 | (0.5, 0.5) | Result card anchor point while open. |
ClosedPosition | UDim2 | UDim2.new(0.5, 0, 0, -4) | Result card position while closed. |
ClosedAnchorPoint | Vector2 | (0.5, 1) | Result card anchor point while closed. |
CornerRadius | UDim | UDim.new(0.06, 0) | Card corner radius. |
StrokeColor | Color3 | Color3.fromRGB(27, 27, 27) | Card outline color. |
StrokeThickness | number | 10 | Card outline thickness. |
SuccessIcon | string | "rbxassetid://106831201567242" | Success status icon. |
FailureIcon | string | "rbxassetid://116200809973713" | Failure status icon. |
IconStartRotation | number | 35 | Icon rotation at the start of its animation. |
IconTargetRotation | number | 0 | Icon rotation at the end of its animation. |
SuccessGradient | ColorSequence | Built-in success gradient | Gradient used for success results. |
FailureGradient | ColorSequence | Built-in failure gradient | Gradient used for failure results. |
GradientRotation | number | 15 | Rotation applied to the status gradients. |
Per-presentation options currently support AutoCloseDuration:
modal:Show("Success", {
AutoCloseDuration = 4,
})
modal:Show(status, options?)
Shows a result. status must be "Success" or "Failure". Showing a new result replaces any currently active presentation.
modal:Show("Success")
modal:Show("Failure", { AutoCloseDuration = 4 })
modal:ShowSuccess(options?)
Convenience method for modal:Show("Success", options).
modal:ShowFailure(options?)
Convenience method for modal:Show("Failure", options).
modal:Close(immediate?)
Closes the current result with the slide-up animation. Pass true to close immediately:
modal:Close(true)
modal:IsOpen() -> boolean
Returns whether a result is currently active.
modal:GetStatus() -> "Success" | "Failure" | nil
Returns the active result status, or nil when no result is open.
modal:Destroy()
Cancels owned tasks and tweens, disconnects input, destroys internal events, and removes the generated UI. A destroyed controller cannot be reused.
Signals
modal.Opened:Connect(function(status)
print("Opened:", status)
end)
modal.Closed:Connect(function(status)
print("Closed:", status)
end)
Opened fires when a presentation begins. Closed fires when its closing transition finishes.
Replacement behavior
Each presentation cancels the previous result's auto-close timer, icon and gradient tasks, closing task, and active tweens. An earlier result therefore cannot close a result shown afterward:
modal:ShowSuccess()
task.wait(0.5)
modal:ShowFailure()
Notes
- The default card is square, centered on screen, and includes an aspect-ratio constraint.
- The success and failure gradients use separate built-in color sequences and share the same shine timing.
- The invisible
DismissAreacaptures click/tap input so dismissal does not fall through to underlying game UI. - The modal does not create a darkened backdrop. Use
PurchaseLoadingOverlayseparately if your design needs one. - Every generated Instance belongs to the controller. The same UI is reused for each presentation and removed by
Destroy().
Project structure
PurchaseResultModal/
โโโ init.luau
โโโ README.md
โโโ wally.toml
โโโ sourcemap.json
โโโ LICENSE
License
MIT โ see LICENSE.
made with โค๏ธ by biotoxin495
Package Details
Install command (Click to copy)
Version
1.0.0
License
MIT
Safe for commercial use
Automated license review โ not legal advice.
